From: DongHun Kwak Date: Fri, 14 Jan 2022 04:50:18 +0000 (+0900) Subject: Imported Upstream version 0.7.7 X-Git-Tag: upstream/0.7.7^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=090f6803360065a42bebd7df6d9099cfccae8181;p=platform%2Fupstream%2Fmultipath-tools.git Imported Upstream version 0.7.7 --- diff --git a/COPYING b/COPYING deleted file mode 100644 index 5bc8fb2..0000000 --- a/COPYING +++ /dev/null @@ -1,481 +0,0 @@ - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 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 library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - 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 Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, 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 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 a program 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. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, 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 companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - 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, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -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. - - 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. - - 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. - - 6. As an exception to the Sections above, you may also compile 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) 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. - - c) 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. - - d) 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 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. - - 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. - - 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 to -this License. - - 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 Library 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. - - 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 - - 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. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library 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. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..0c0462d --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +LICENSES/LGPL-2.0 \ No newline at end of file diff --git a/LICENSES/GPL-2.0 b/LICENSES/GPL-2.0 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSES/GPL-2.0 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + 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 Lesser 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. + + , 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 Lesser General +Public License instead of this License. diff --git a/LICENSES/GPL-3.0 b/LICENSES/GPL-3.0 new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSES/GPL-3.0 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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 Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSES/LGPL-2.0 b/LICENSES/LGPL-2.0 new file mode 100644 index 0000000..5bc8fb2 --- /dev/null +++ b/LICENSES/LGPL-2.0 @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 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 library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + 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 Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 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 a program 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. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + 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, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +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. + + 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. + + 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. + + 6. As an exception to the Sections above, you may also compile 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) 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. + + c) 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. + + d) 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 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. + + 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. + + 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 to +this License. + + 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 Library 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. + + 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 + + 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-2.1 b/LICENSES/LGPL-2.1 new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/LICENSES/LGPL-2.1 @@ -0,0 +1,502 @@ + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile.inc b/Makefile.inc index 5d6123d..57a1835 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -14,6 +14,9 @@ # # Uncomment to disable libdmmp support # ENABLE_LIBDMMP = 0 +# +# Uncomment to disable dmevents polling support +# ENABLE_DMEVENTS_POLL = 0 ifeq ($(TOPDIR),) TOPDIR = .. diff --git a/README b/README index 89bab74..2fc4a81 100644 --- a/README +++ b/README @@ -51,3 +51,10 @@ Maintainer ========== Christophe Varoqui Device-mapper development mailing list + +Licence +======= +The multipath-tools source code is covered by several different +licences. Refer to the individual source files for details. +Source files which do not specify a licence are shipped under +LGPL-2.0 (see LICENSES/LGPL-2.0). diff --git a/libdmmp/libdmmp/libdmmp.h b/libdmmp/libdmmp/libdmmp.h index e157982..6e6610d 100644 --- a/libdmmp/libdmmp/libdmmp.h +++ b/libdmmp/libdmmp/libdmmp.h @@ -389,7 +389,7 @@ DMMP_DLL_EXPORT const char *dmmp_mpath_name_get(struct dmmp_mpath *dmmp_mp); * dmmp_mpath_kdev_name_get() - Retrieve kernel DEVNAME of certain mpath. * * Retrieve DEVNAME name used by kernel uevent of specified mpath. - * Example: 'dm-1'. + * For example: 'dm-1'. * * @dmmp_mp: * Pointer of 'struct dmmp_mpath'. @@ -553,8 +553,8 @@ DMMP_DLL_EXPORT void dmmp_path_array_get(struct dmmp_path_group *dmmp_pg, /** * dmmp_path_blk_name_get() - Retrieve block name. * - * Retrieve block name of certain path. The example of block names are 'sda', - * 'nvme0n1'. + * Retrieve block name of certain path. The example of block names are "sda", + * "nvme0n1". * * @dmmp_p: * Pointer of 'struct dmmp_path'. diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c index 29d148c..61e6a98 100644 --- a/libmpathcmd/mpath_cmd.c +++ b/libmpathcmd/mpath_cmd.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This file is part of the device-mapper multipath userspace tools. + * + * This program 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 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + #include #include #include diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h index b57b708..aaa8da9 100644 --- a/libmpathcmd/mpath_cmd.h +++ b/libmpathcmd/mpath_cmd.h @@ -5,7 +5,7 @@ * * This program 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 + * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 5199e42..907a17c 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -909,7 +909,7 @@ int update_map_pr(struct multipath *mpp) if (resp->prin_descriptor.prin_readkeys.additional_length == 0 ) { - condlog(0,"%s: No key found. Device may not be registered. ", mpp->alias); + condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias); free(resp); return MPATH_PR_SUCCESS; } diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c index dbed4ca..6dd7403 100644 --- a/libmpathpersist/mpath_pr_ioctl.c +++ b/libmpathpersist/mpath_pr_ioctl.c @@ -77,14 +77,14 @@ int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope, cdb[8] = (unsigned char)(paramlen & 0xff); retry : - condlog(3, "%s: rq_servact = %d", dev, rq_servact); - condlog(3, "%s: rq_scope = %d ", dev, rq_scope); - condlog(3, "%s: rq_type = %d ", dev, rq_type); - condlog(3, "%s: paramlen = %d", dev, paramlen); + condlog(4, "%s: rq_servact = %d", dev, rq_servact); + condlog(4, "%s: rq_scope = %d ", dev, rq_scope); + condlog(4, "%s: rq_type = %d ", dev, rq_type); + condlog(4, "%s: paramlen = %d", dev, paramlen); if (noisy) { - condlog(3, "%s: Persistent Reservation OUT parameter:", dev); + condlog(4, "%s: Persistent Reservation OUT parameter:", dev); dumpHex((const char *)paramp, paramlen,1); } @@ -113,7 +113,7 @@ retry : return ret; } - condlog(2, "%s: Duration=%u (ms)", dev, io_hdr.duration); + condlog(4, "%s: Duration=%u (ms)", dev, io_hdr.duration); status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy); condlog(3, "%s: status = %d", dev, status); @@ -121,7 +121,7 @@ retry : if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; - condlog(2, "%s: retrying for Unit Attention. Remaining retries = %d", + condlog(3, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } @@ -131,7 +131,7 @@ retry : { usleep(1000); --retry; - condlog(2, "%s: retrying for sense 02/04/07." + condlog(3, "%s: retrying for sense 02/04/07." " Remaining retries = %d", dev, retry); goto retry; } @@ -224,7 +224,7 @@ void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy) if (pr_buff->prin_descriptor.prin_readfd.number_of_descriptor == 0) { - condlog(2, "No registration or resrvation found."); + condlog(3, "No registration or resrvation found."); return; } @@ -351,15 +351,15 @@ retry : got = mx_resp_len - io_hdr.resid; - condlog(2, "%s: duration = %u (ms)", dev, io_hdr.duration); - condlog(2, "%s: persistent reservation in: requested %d bytes but got %d bytes)", dev, mx_resp_len, got); + condlog(3, "%s: duration = %u (ms)", dev, io_hdr.duration); + condlog(4, "%s: persistent reservation in: requested %d bytes but got %d bytes)", dev, mx_resp_len, got); status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; - condlog(2, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); + condlog(3, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } @@ -368,7 +368,7 @@ retry : { usleep(1000); --retry; - condlog(2, "%s: retrying for 02/04/07. Remaining retries = %d", dev, retry); + condlog(3, "%s: retrying for 02/04/07. Remaining retries = %d", dev, retry); goto retry; } @@ -414,7 +414,7 @@ int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, case SAM_STAT_GOOD: break; case SAM_STAT_CHECK_CONDITION: - condlog(2, "%s: Sense_Key=%02x, ASC=%02x ASCQ=%02x", + condlog(3, "%s: Sense_Key=%02x, ASC=%02x ASCQ=%02x", dev, Sensedata->Sense_Key, Sensedata->ASC, Sensedata->ASCQ); switch(Sensedata->Sense_Key) { @@ -471,11 +471,11 @@ int mpath_isLittleEndian(void) int num = 1; if(*(char *)&num == 1) { - condlog(2, "Little-Endian"); + condlog(4, "Little-Endian"); } else { - condlog(2, "Big-Endian"); + condlog(4, "Big-Endian"); } return 0; } diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 806aaa2..f51786d 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -42,7 +42,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \ pgpolicies.o debug.o defaults.o uevent.o time-util.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \ - lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \ + lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \ io_err_stat.o dm-generic.o generic.o foreign.o all: $(LIBS) diff --git a/libmultipath/callout.c b/libmultipath/callout.c index dc18e02..d5ca27b 100644 --- a/libmultipath/callout.c +++ b/libmultipath/callout.c @@ -18,6 +18,7 @@ #include "vector.h" #include "structs.h" #include "util.h" +#include "callout.h" #include "debug.h" int execute_program(char *path, char *value, int len) diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c index b1d99b4..4ff54f4 100644 --- a/libmultipath/checkers/rbd.c +++ b/libmultipath/checkers/rbd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "rados/librados.h" @@ -517,6 +518,7 @@ static void cleanup_func(void *data) pthread_spin_unlock(&ct->hldr_lock); if (!holders) cleanup_context(ct); + rcu_unregister_thread(); } static void *rbd_thread(void *ctx) @@ -524,11 +526,12 @@ static void *rbd_thread(void *ctx) struct rbd_checker_context *ct = ctx; int state; + /* This thread can be canceled, so setup clean up */ + rbd_thread_cleanup_push(ct) + rcu_register_thread(); condlog(3, "rbd%d: thread starting up", ct->rbd_bus_id); ct->message[0] = '\0'; - /* This thread can be canceled, so setup clean up */ - rbd_thread_cleanup_push(ct) /* checker start up */ pthread_mutex_lock(&ct->lock); diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index 9155960..eb3348d 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "checkers.h" @@ -220,6 +221,7 @@ static void cleanup_func(void *data) holders = uatomic_sub_return(&ct->holders, 1); if (!holders) cleanup_context(ct); + rcu_unregister_thread(); } static void copy_msg_to_tcc(void *ct_p, const char *msg) @@ -237,11 +239,12 @@ static void *tur_thread(void *ctx) int state, running; char devt[32]; - condlog(3, "%s: tur checker starting up", - tur_devt(devt, sizeof(devt), ct)); - /* This thread can be canceled, so setup clean up */ tur_thread_cleanup_push(ct); + rcu_register_thread(); + + condlog(3, "%s: tur checker starting up", + tur_devt(devt, sizeof(devt), ct)); /* TUR checker start up */ pthread_mutex_lock(&ct->lock); diff --git a/libmultipath/config.h b/libmultipath/config.h index a20ac2a..6e69a37 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -139,7 +139,6 @@ struct config { int max_fds; int force_reload; int queue_without_daemon; - int ignore_wwids; int checker_timeout; int flush_on_last_del; int attribute_flags; @@ -168,7 +167,6 @@ struct config { int strict_timing; int retrigger_tries; int retrigger_delay; - int ignore_new_devs; int delayed_reconfig; int uev_wait_timeout; int skip_kpartx; @@ -176,6 +174,7 @@ struct config { int remove_retries; int max_sectors_kb; int ghost_delay; + int find_multipaths_timeout; unsigned int version[3]; char * multipath_dir; @@ -232,6 +231,6 @@ struct config *load_config (char * file); struct config * alloc_config (void); void free_config (struct config * conf); extern struct config *get_multipath_config(void); -extern void put_multipath_config(struct config *); +extern void put_multipath_config(void *); #endif diff --git a/libmultipath/configure.c b/libmultipath/configure.c index fa6e21c..5796683 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "mpath_cmd.h" @@ -288,6 +289,8 @@ int setup_map(struct multipath *mpp, char *params, int params_size, * propsel.c if in doubt. */ conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); + select_pgfailback(conf, mpp); select_pgpolicy(conf, mpp); select_selector(conf, mpp); @@ -316,7 +319,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, select_flush_on_last_del(conf, mpp); sysfs_set_scsi_tmo(mpp, conf->checkint); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (mpp->marginal_path_double_failed_time > 0 && mpp->marginal_path_err_sample_time > 0 && @@ -442,6 +445,62 @@ trigger_udev_change(const struct multipath *mpp) udev_device_unref(udd); } +void +trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) +{ + struct pathgroup *pgp; + struct path *pp; + int i, j; + /* + * If a path changes from multipath to non-multipath, we must + * synthesize an artificial "add" event, otherwise the LVM2 rules + * (69-lvm2-lvmetad.rules) won't pick it up. Otherwise, we'd just + * irritate ourselves with an "add", so use "change". + */ + const char *action = is_mpath ? "change" : "add"; + + if (!mpp || !mpp->pg) + return; + + vector_foreach_slot (mpp->pg, pgp, i) { + if (!pgp->paths) + continue; + vector_foreach_slot(pgp->paths, pp, j) { + const char *env; + + if (!pp->udev) + continue; + /* + * Paths that are already classified as multipath + * members don't need another uevent. + */ + env = udev_device_get_property_value( + pp->udev, "DM_MULTIPATH_DEVICE_PATH"); + + if (is_mpath && env != NULL && !strcmp(env, "1")) { + /* + * If FIND_MULTIPATHS_WAIT_UNTIL is not "0", + * path is in "maybe" state and timer is running + * Send uevent now (see multipath.rules). + */ + env = udev_device_get_property_value( + pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL"); + if (env == NULL || !strcmp(env, "0")) + continue; + } else if (!is_mpath && + (env == NULL || !strcmp(env, "0"))) + continue; + + condlog(3, "triggering %s uevent for %s (is %smultipath member)", + action, pp->dev, is_mpath ? "" : "no "); + sysfs_attr_set_value(pp->udev, "uevent", + action, strlen(action)); + } + } + + mpp->needs_paths_uevent = 0; +} + static int is_mpp_known_to_udev(const struct multipath *mpp) { @@ -742,14 +801,16 @@ int domap(struct multipath *mpp, char *params, int is_daemon) { int r = DOMAP_FAIL; struct config *conf; + int verbosity; /* * last chance to quit before touching the devmaps */ if (mpp->action == ACT_DRY_RUN) { conf = get_multipath_config(); - print_multipath_topology(mpp, conf->verbosity); + verbosity = conf->verbosity; put_multipath_config(conf); + print_multipath_topology(mpp, verbosity); return DOMAP_DRY; } @@ -807,16 +868,18 @@ int domap(struct multipath *mpp, char *params, int is_daemon) case ACT_RENAME: conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = dm_rename(mpp->alias_old, mpp->alias, conf->partition_delim, mpp->skip_kpartx); - put_multipath_config(conf); + pthread_cleanup_pop(1); break; case ACT_FORCERENAME: conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = dm_rename(mpp->alias_old, mpp->alias, conf->partition_delim, mpp->skip_kpartx); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (r) { sysfs_set_max_sectors_kb(mpp, 1); if (mpp->ghost_delay_tick > 0 && @@ -836,8 +899,10 @@ int domap(struct multipath *mpp, char *params, int is_daemon) * succeeded */ mpp->force_udev_reload = 0; - if (mpp->action == ACT_CREATE) - remember_wwid(mpp->wwid); + if (mpp->action == ACT_CREATE && + (remember_wwid(mpp->wwid) == 1 || + mpp->needs_paths_uevent)) + trigger_paths_udev_change(mpp, true); if (!is_daemon) { /* multipath client mode */ dm_switchgroup(mpp->alias, mpp->bestpg); @@ -862,7 +927,10 @@ int domap(struct multipath *mpp, char *params, int is_daemon) } dm_setgeometry(mpp); return DOMAP_OK; - } + } else if (r == DOMAP_FAIL && mpp->action == ACT_CREATE && + mpp->needs_paths_uevent) + trigger_paths_udev_change(mpp, false); + return DOMAP_FAIL; } @@ -952,17 +1020,19 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, } } vector_foreach_slot (pathvec, pp1, k) { + int invalid = 0; /* skip this path for some reason */ /* 1. if path has no unique id or wwid blacklisted */ conf = get_multipath_config(); - if (strlen(pp1->wwid) == 0 || - filter_path(conf, pp1) > 0) { - put_multipath_config(conf); + pthread_cleanup_push(put_multipath_config, conf); + if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) { orphan_path(pp1, "wwid blacklisted"); continue; } - put_multipath_config(conf); /* 2. if path already coalesced */ if (pp1->mpp) @@ -979,7 +1049,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, continue; /* If find_multipaths was selected check if the path is valid */ - if (!refwwid && !should_multipath(pp1, pathvec)) { + if (!refwwid && !should_multipath(pp1, pathvec, curmp)) { orphan_path(pp1, "only one path"); continue; } @@ -1082,9 +1152,12 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, } if (!is_daemon && mpp->action != ACT_NOTHING) { + int verbosity; + conf = get_multipath_config(); - print_multipath_topology(mpp, conf->verbosity); + verbosity = conf->verbosity; put_multipath_config(conf); + print_multipath_topology(mpp, verbosity); } if (newmp) { @@ -1174,6 +1247,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, char * refwwid = NULL, tmpwwid[WWID_SIZE]; int flags = DI_SYSFS | DI_WWID; struct config *conf; + int invalid = 0; if (!wwid) return 1; @@ -1201,9 +1275,10 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, return 1; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); - put_multipath_config(conf); + pthread_cleanup_pop(1); udev_device_unref(udevice); if (!pp) { if (ret == 1) @@ -1213,12 +1288,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, } } conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev) > 0) { - put_multipath_config(conf); + filter_property(conf, pp->udev) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) return 2; - } - put_multipath_config(conf); refwwid = pp->wwid; goto out; @@ -1239,9 +1315,10 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, return 1; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); - put_multipath_config(conf); + pthread_cleanup_pop(1); udev_device_unref(udevice); if (!pp) { if (ret == 1) @@ -1251,12 +1328,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, } } conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev) > 0) { - put_multipath_config(conf); + filter_property(conf, pp->udev) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) return 2; - } - put_multipath_config(conf); refwwid = pp->wwid; goto out; } @@ -1268,22 +1346,24 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, return 1; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); + pthread_cleanup_pop(1); udev_device_unref(udevice); if (!pp) { if (ret == 1) - condlog(0, "%s: can't store path info", - dev); - put_multipath_config(conf); + condlog(0, "%s: can't store path info", dev); return ret; } + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev) > 0) { - put_multipath_config(conf); + filter_property(conf, pp->udev) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) return 2; - } - put_multipath_config(conf); refwwid = pp->wwid; goto out; } @@ -1291,6 +1371,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, if (dev_type == DEV_DEVMAP) { conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { refwwid = tmpwwid; goto check; @@ -1302,7 +1383,6 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, if (get_user_friendly_wwid(dev, tmpwwid, conf->bindings_file) == 0) { refwwid = tmpwwid; - put_multipath_config(conf); goto check; } @@ -1318,14 +1398,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, refwwid = dev; check: - if (refwwid && strlen(refwwid)) { - if (filter_wwid(conf->blist_wwid, conf->elist_wwid, - refwwid, NULL) > 0) { - put_multipath_config(conf); - return 2; - } - } - put_multipath_config(conf); + if (refwwid && strlen(refwwid) && + filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid, + NULL) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) + return 2; } out: if (refwwid && strlen(refwwid)) { @@ -1347,8 +1426,9 @@ int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, if (refresh) { vector_foreach_slot (mpp->paths, pp, i) { struct config *conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = pathinfo(pp, conf, DI_PRIO); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (r) { condlog(2, "%s: failed to refresh pathinfo", mpp->alias); diff --git a/libmultipath/configure.h b/libmultipath/configure.h index 27a7e6f..8b56d33 100644 --- a/libmultipath/configure.h +++ b/libmultipath/configure.h @@ -37,3 +37,4 @@ int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type, vector pathvec, char **wwid); int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon); struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); +void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath); diff --git a/libmultipath/debug.c b/libmultipath/debug.c index f95a3e5..cbf1e57 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -12,6 +12,7 @@ #include "vector.h" #include "config.h" #include "defaults.h" +#include "debug.h" void dlog (int sink, int prio, const char * fmt, ...) { diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c index e665325..7130e56 100644 --- a/libmultipath/defaults.c +++ b/libmultipath/defaults.c @@ -3,6 +3,7 @@ */ #include +#include "defaults.h" #include "memory.h" char * diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 2b270ca..d7b87b4 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -17,7 +17,7 @@ #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF #define DEFAULT_VERBOSITY 2 #define DEFAULT_REASSIGN_MAPS 0 -#define DEFAULT_FIND_MULTIPATHS 0 +#define DEFAULT_FIND_MULTIPATHS FIND_MULTIPATHS_STRICT #define DEFAULT_FAST_IO_FAIL 5 #define DEFAULT_DEV_LOSS_TMO 600 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_ON @@ -41,6 +41,8 @@ #define DEFAULT_DISABLE_CHANGED_WWIDS 1 #define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF #define DEFAULT_GHOST_DELAY GHOST_DELAY_OFF +#define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10 +#define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) @@ -53,5 +55,6 @@ #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" #define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys" #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" +#define MULTIPATH_SHM_BASE "/dev/shm/multipath/" char * set_default (char * str); diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 767d87c..f2befad 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -22,6 +22,7 @@ #include "devmapper.h" #include "sysfs.h" #include "config.h" +#include "wwids.h" #include "log_pthread.h" #include @@ -95,6 +96,7 @@ dm_write_log (int level, const char *file, int line, const char *f, ...) void dm_init(int v) { + dm_conf_verbosity = v; dm_log_init(&dm_write_log); dm_log_init_verbose(v + 3); } @@ -129,7 +131,27 @@ dm_lib_prereq (void) } int -dm_drv_version (unsigned int * version, char * str) +dm_drv_version(unsigned int *v) +{ + char buff[64]; + + v[0] = 0; + v[1] = 0; + v[2] = 0; + + if (!dm_driver_version(buff, sizeof(buff))) { + condlog(0, "cannot get kernel dm version"); + return 1; + } + if (sscanf(buff, "%u.%u.%u ", &v[0], &v[1], &v[2]) != 3) { + condlog(0, "invalid kernel dm version '%s'", buff); + return 1; + } + return 0; +} + +int +dm_tgt_version (unsigned int * version, char * str) { int r = 2; struct dm_task *dmt; @@ -176,13 +198,13 @@ out: } static int -dm_drv_prereq (unsigned int *ver) +dm_tgt_prereq (unsigned int *ver) { unsigned int minv[3] = {1, 0, 3}; unsigned int version[3] = {0, 0, 0}; unsigned int * v = version; - if (dm_drv_version(v, TGT_MPATH)) { + if (dm_tgt_version(v, TGT_MPATH)) { /* in doubt return not capable */ return 1; } @@ -207,7 +229,7 @@ static int dm_prereq(unsigned int *v) { if (dm_lib_prereq()) return 1; - return dm_drv_prereq(v); + return dm_tgt_prereq(v); } static int libmp_dm_udev_sync = 0; @@ -220,12 +242,16 @@ void libmp_udev_set_sync_support(int on) void libmp_dm_init(void) { struct config *conf; + int verbosity; + unsigned int version[3]; conf = get_multipath_config(); - dm_init(conf->verbosity); - if (dm_prereq(conf->version)) - exit(1); + verbosity = conf->verbosity; + memcpy(version, conf->version, sizeof(version)); put_multipath_config(conf); + dm_init(verbosity); + if (dm_prereq(version)) + exit(1); dm_udev_set_sync_support(libmp_dm_udev_sync); } @@ -390,8 +416,12 @@ int dm_addmap_create (struct multipath *mpp, char * params) int err; if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro, - udev_flags)) + udev_flags)) { + if (unmark_failed_wwid(mpp->wwid) == + WWID_FAILED_CHANGED) + mpp->needs_paths_uevent = 1; return 1; + } /* * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD. * Failing the second part leaves an empty map. Clean it up. @@ -407,6 +437,8 @@ int dm_addmap_create (struct multipath *mpp, char * params) break; } } + if (mark_failed_wwid(mpp->wwid) == WWID_FAILED_CHANGED) + mpp->needs_paths_uevent = 1; return 0; } diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 8c8ea6c..db75526 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -31,7 +31,8 @@ void dm_init(int verbosity); void libmp_dm_init(void); void libmp_udev_set_sync_support(int on); struct dm_task *libmp_dm_task_create(int task); -int dm_drv_version (unsigned int * version, char * str); +int dm_drv_version (unsigned int * version); +int dm_tgt_version (unsigned int * version, char * str); int dm_simplecmd_flush (int, const char *, uint16_t); int dm_simplecmd_noflush (int, const char *, uint16_t); int dm_addmap_create (struct multipath *mpp, char *params); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index ea273dd..4040611 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -23,6 +23,7 @@ #include #include #include "mpath_cmd.h" +#include "dict.h" static int set_int(vector strvec, void *ptr) @@ -100,12 +101,16 @@ print_int (char *buff, int len, long v) static int print_nonzero (char *buff, int len, long v) { + if (!v) + return 0; return snprintf(buff, len, "%li", v); } static int print_str (char *buff, int len, const char *ptr) { + if (!ptr) + return 0; return snprintf(buff, len, "\"%s\"", ptr); } @@ -119,6 +124,8 @@ print_yes_no (char *buff, int len, long v) static int print_yes_no_undef (char *buff, int len, long v) { + if (!v) + return 0; return snprintf(buff, len, "\"%s\"", (v == YNU_NO)? "no" : "yes"); } @@ -233,8 +240,52 @@ declare_def_snprint(multipath_dir, print_str) declare_def_handler(partition_delim, set_str) declare_def_snprint(partition_delim, print_str) -declare_def_handler(find_multipaths, set_yes_no) -declare_def_snprint(find_multipaths, print_yes_no) +static const char * const find_multipaths_optvals[] = { + [FIND_MULTIPATHS_OFF] = "off", + [FIND_MULTIPATHS_ON] = "on", + [FIND_MULTIPATHS_STRICT] = "strict", + [FIND_MULTIPATHS_GREEDY] = "greedy", + [FIND_MULTIPATHS_SMART] = "smart", +}; + +static int +def_find_multipaths_handler(struct config *conf, vector strvec) +{ + char *buff; + int i; + + if (set_yes_no_undef(strvec, &conf->find_multipaths) == 0 && + conf->find_multipaths != YNU_UNDEF) + return 0; + + buff = set_value(strvec); + if (!buff) + return 1; + + for (i = FIND_MULTIPATHS_OFF; i < __FIND_MULTIPATHS_LAST; i++) { + if (find_multipaths_optvals[i] != NULL && + !strcmp(buff, find_multipaths_optvals[i])) { + conf->find_multipaths = i; + break; + } + } + + if (conf->find_multipaths == YNU_UNDEF) { + condlog(0, "illegal value for find_multipaths: %s", buff); + conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; + } + + FREE(buff); + return 0; +} + +static int +snprint_def_find_multipaths(struct config *conf, char *buff, int len, + const void *data) +{ + return print_str(buff, len, + find_multipaths_optvals[conf->find_multipaths]); +} declare_def_handler(selector, set_str) declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) @@ -441,6 +492,10 @@ declare_hw_snprint(max_sectors_kb, print_nonzero) declare_mp_handler(max_sectors_kb, set_int) declare_mp_snprint(max_sectors_kb, print_nonzero) +declare_def_handler(find_multipaths_timeout, set_int) +declare_def_snprint_defint(find_multipaths_timeout, print_int, + DEFAULT_FIND_MULTIPATHS_TIMEOUT) + static int def_config_dir_handler(struct config *conf, vector strvec) { @@ -664,6 +719,8 @@ set_dev_loss(vector strvec, void *ptr) int print_dev_loss(char * buff, int len, unsigned long v) { + if (!v) + return 0; if (v >= MAX_DEV_LOSS_TMO) return snprintf(buff, len, "\"infinity\""); return snprintf(buff, len, "%lu", v); @@ -1475,6 +1532,9 @@ init_keywords(vector keywords) install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); install_keyword("ghost_delay", &def_ghost_delay_handler, &snprint_def_ghost_delay); + install_keyword("find_multipaths_timeout", + &def_find_multipaths_timeout_handler, + &snprint_def_find_multipaths_timeout); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); diff --git a/libmultipath/dict.h b/libmultipath/dict.h index 0442227..7564892 100644 --- a/libmultipath/dict.h +++ b/libmultipath/dict.h @@ -9,12 +9,12 @@ void init_keywords(vector keywords); int get_sys_max_fds(int *); -int print_rr_weight (char * buff, int len, void *ptr); -int print_pgfailback (char * buff, int len, void *ptr); -int print_pgpolicy(char * buff, int len, void *ptr); -int print_no_path_retry(char * buff, int len, void *ptr); -int print_fast_io_fail(char * buff, int len, void *ptr); -int print_dev_loss(char * buff, int len, void *ptr); +int print_rr_weight(char *buff, int len, long v); +int print_pgfailback(char *buff, int len, long v); +int print_pgpolicy(char *buff, int len, long v); +int print_no_path_retry(char *buff, int len, long v); +int print_fast_io_fail(char *buff, int len, long v); +int print_dev_loss(char *buff, int len, unsigned long v); int print_reservation_key(char * buff, int len, struct be64 key, int source); -int print_off_int_undef(char * buff, int len, void *ptr); +int print_off_int_undef(char *buff, int len, long v); #endif /* _DICT_H */ diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 9f2a9c9..1ef1dfa 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -170,10 +170,11 @@ path_discovery (vector pathvec, int flag) if(devtype && !strncmp(devtype, "disk", 4)) { total_paths++; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (path_discover(pathvec, conf, udevice, flag) == PATHINFO_OK) num_paths++; - put_multipath_config(conf); + pthread_cleanup_pop(1); } udev_device_unref(udevice); } @@ -1617,6 +1618,7 @@ get_prio (struct path * pp) { struct prio * p; struct config *conf; + int checker_timeout; if (!pp) return 0; @@ -1624,9 +1626,10 @@ get_prio (struct path * pp) p = &pp->prio; if (!prio_selected(p)) { conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); select_detect_prio(conf, pp); select_prio(conf, pp); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (!prio_selected(p)) { condlog(3, "%s: no prio selected", pp->dev); pp->priority = PRIO_UNDEF; @@ -1634,8 +1637,9 @@ get_prio (struct path * pp) } } conf = get_multipath_config(); - pp->priority = prio_getprio(p, pp, conf->checker_timeout); + checker_timeout = conf->checker_timeout; put_multipath_config(conf); + pp->priority = prio_getprio(p, pp, checker_timeout); if (pp->priority < 0) { condlog(3, "%s: %s prio error", pp->dev, prio_name(p)); pp->priority = PRIO_UNDEF; @@ -1849,8 +1853,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev) if (!pp->uid_attribute && !pp->getuid) { conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); select_getuid(conf, pp); - put_multipath_config(conf); + pthread_cleanup_pop(1); } memset(pp->wwid, 0, WWID_SIZE); diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index 783c934..620f507 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -13,6 +13,7 @@ #include "structs.h" #include "util.h" #include "debug.h" +#include "dmparser.h" #define WORD_SIZE 64 diff --git a/libmultipath/file.c b/libmultipath/file.c index e4951c9..8727f16 100644 --- a/libmultipath/file.c +++ b/libmultipath/file.c @@ -36,8 +36,8 @@ * See the file COPYING included with this distribution for more details. */ -static int -ensure_directories_exist(char *str, mode_t dir_mode) +int +ensure_directories_exist(const char *str, mode_t dir_mode) { char *pathname; char *end; @@ -80,7 +80,7 @@ sigalrm(int sig) } static int -lock_file(int fd, char *file_name) +lock_file(int fd, const char *file_name) { struct sigaction act, oldact; sigset_t set, oldset; @@ -118,7 +118,7 @@ lock_file(int fd, char *file_name) } int -open_file(char *file, int *can_write, char *header) +open_file(const char *file, int *can_write, const char *header) { int fd; struct stat s; diff --git a/libmultipath/file.h b/libmultipath/file.h index 4f96dbf..29520c7 100644 --- a/libmultipath/file.h +++ b/libmultipath/file.h @@ -6,6 +6,7 @@ #define _FILE_H #define FILE_TIMEOUT 30 -int open_file(char *file, int *can_write, char *header); +int ensure_directories_exist(const char *str, mode_t dir_mode); +int open_file(const char *file, int *can_write, const char *header); #endif /* _FILE_H */ diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index fe71d14..88b4700 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -7,6 +7,7 @@ #include "config.h" #include "pgpolicies.h" #include "prio.h" +#include "hwtable.h" /* * Tuning suggestions on these parameters should go to @@ -404,7 +405,7 @@ static struct hwentry default_hw[] = { }, { /* AMS 2000 and HUS 100 families */ - .vendor = "(HITACHI|HP)", + .vendor = "HITACHI", .product = "^DF", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = GROUP_BY_PRIO, diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index 035ae5d..bb35dfc 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -14,13 +14,14 @@ #include "log.h" #include "lock.h" -pthread_t log_thr; +static pthread_t log_thr; -pthread_mutex_t logq_lock; -pthread_mutex_t logev_lock; -pthread_cond_t logev_cond; +static pthread_mutex_t logq_lock; +static pthread_mutex_t logev_lock; +static pthread_cond_t logev_cond; -int logq_running; +static int logq_running; +static int log_messages_pending; void log_safe (int prio, const char * fmt, va_list ap) { @@ -34,23 +35,11 @@ void log_safe (int prio, const char * fmt, va_list ap) pthread_mutex_unlock(&logq_lock); pthread_mutex_lock(&logev_lock); + log_messages_pending = 1; pthread_cond_signal(&logev_cond); pthread_mutex_unlock(&logev_lock); } -void log_thread_flush (void) -{ - int empty; - - do { - pthread_mutex_lock(&logq_lock); - empty = log_dequeue(la->buff); - pthread_mutex_unlock(&logq_lock); - if (!empty) - log_syslog(la->buff); - } while (empty == 0); -} - static void flush_logqueue (void) { int empty; @@ -77,12 +66,14 @@ static void * log_thread (void * et) while (1) { pthread_mutex_lock(&logev_lock); - pthread_cond_wait(&logev_cond, &logev_lock); + if (logq_running && !log_messages_pending) + pthread_cond_wait(&logev_cond, &logev_lock); + log_messages_pending = 0; running = logq_running; pthread_mutex_unlock(&logev_lock); if (!running) break; - log_thread_flush(); + flush_logqueue(); } return NULL; } @@ -107,6 +98,15 @@ void log_thread_start (pthread_attr_t *attr) return; } +void log_thread_reset (void) +{ + logdbg(stderr,"resetting log\n"); + + pthread_mutex_lock(&logq_lock); + log_reset("multipathd"); + pthread_mutex_unlock(&logq_lock); +} + void log_thread_stop (void) { logdbg(stderr,"enter log_thread_stop\n"); diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h index e5c6499..7e138a0 100644 --- a/libmultipath/log_pthread.h +++ b/libmultipath/log_pthread.h @@ -3,17 +3,9 @@ #include -extern pthread_t log_thr; - -extern pthread_mutex_t logq_lock; -extern pthread_mutex_t logev_lock; -extern pthread_cond_t logev_cond; - -extern int logq_running; - void log_safe(int prio, const char * fmt, va_list ap); void log_thread_start(pthread_attr_t *attr); +void log_thread_reset (void); void log_thread_stop(void); -void log_thread_flush(void); #endif /* _LOG_PTHREAD_H */ diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 2f9ab6e..b8b7e0d 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -171,8 +171,9 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, break; case 'v': conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = kw->print(conf, buff + fwd, len - fwd, data); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (!r) { /* no output if no value */ buff[0] = '\0'; return 0; diff --git a/libmultipath/print.c b/libmultipath/print.c index d532994..b1844c9 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -632,6 +632,12 @@ snprint_path_foreign (char * buff, size_t len, const struct path * pp) return snprintf(buff, len, "%s", "--"); } +static int +snprint_path_failures(char * buff, size_t len, const struct path * pp) +{ + return snprint_int(buff, len, pp->failcount); +} + struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, @@ -680,6 +686,7 @@ struct path_data pd[] = { {'r', "target WWPN", 0, snprint_tgt_wwpn}, {'a', "host adapter", 0, snprint_host_adapter}, {'G', "foreign", 0, snprint_path_foreign}, + {'0', "failures", 0, snprint_path_failures}, {0, NULL, 0 , NULL} }; diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 58a6a42..627d366 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -22,6 +22,7 @@ #include "sysfs.h" #include "prioritizers/alua_rtpg.h" #include "prkey.h" +#include "propsel.h" #include #include @@ -42,31 +43,46 @@ do { \ goto out; \ } \ } while(0) + +static const char default_origin[] = "(setting: multipath internal)"; +static const char hwe_origin[] = + "(setting: storage device configuration)"; +static const char multipaths_origin[] = + "(setting: multipath.conf multipaths section)"; +static const char conf_origin[] = + "(setting: multipath.conf defaults/devices section)"; +static const char overrides_origin[] = + "(setting: multipath.conf overrides section)"; +static const char cmdline_origin[] = + "(setting: multipath command line [-p] flag)"; +static const char autodetect_origin[] = + "(setting: storage device autodetected)"; + #define do_default(dest, value) \ do { \ dest = value; \ - origin = "(setting: multipath internal)"; \ + origin = default_origin; \ } while(0) #define mp_set_mpe(var) \ -do_set(var, mp->mpe, mp->var, "(setting: multipath.conf multipaths section)") +do_set(var, mp->mpe, mp->var, multipaths_origin) #define mp_set_hwe(var) \ -do_set(var, mp->hwe, mp->var, "(setting: storage device configuration)") +do_set(var, mp->hwe, mp->var, hwe_origin) #define mp_set_ovr(var) \ -do_set(var, conf->overrides, mp->var, "(setting: multipath.conf overrides section)") +do_set(var, conf->overrides, mp->var, overrides_origin) #define mp_set_conf(var) \ -do_set(var, conf, mp->var, "(setting: multipath.conf defaults/devices section)") +do_set(var, conf, mp->var, conf_origin) #define mp_set_default(var, value) \ do_default(mp->var, value) #define pp_set_mpe(var) \ -do_set(var, mpe, pp->var, "(setting: multipath.conf multipaths section)") +do_set(var, mpe, pp->var, multipaths_origin) #define pp_set_hwe(var) \ -do_set(var, pp->hwe, pp->var, "(setting: storage device configuration)") +do_set(var, pp->hwe, pp->var, hwe_origin) #define pp_set_conf(var) \ -do_set(var, conf, pp->var, "(setting: multipath.conf defaults/devices section)") +do_set(var, conf, pp->var, conf_origin) #define pp_set_ovr(var) \ -do_set(var, conf->overrides, pp->var, "(setting: multipath.conf overrides section)") +do_set(var, conf->overrides, pp->var, overrides_origin) #define pp_set_default(var, value) \ do_default(pp->var, value) @@ -97,7 +113,7 @@ do { \ int select_mode(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; set_attr_mpe(mode, ATTR_MODE); set_attr_conf(mode, ATTR_MODE); @@ -110,7 +126,7 @@ out: int select_uid(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; set_attr_mpe(uid, ATTR_UID); set_attr_conf(uid, ATTR_UID); @@ -123,7 +139,7 @@ out: int select_gid(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; set_attr_mpe(gid, ATTR_GID); set_attr_conf(gid, ATTR_GID); @@ -141,7 +157,8 @@ out: */ int select_rr_weight(struct config *conf, struct multipath * mp) { - char *origin, buff[13]; + const char *origin; + char buff[13]; mp_set_mpe(rr_weight); mp_set_ovr(rr_weight); @@ -149,14 +166,15 @@ int select_rr_weight(struct config *conf, struct multipath * mp) mp_set_conf(rr_weight); mp_set_default(rr_weight, DEFAULT_RR_WEIGHT); out: - print_rr_weight(buff, 13, &mp->rr_weight); + print_rr_weight(buff, 13, mp->rr_weight); condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin); return 0; } int select_pgfailback(struct config *conf, struct multipath * mp) { - char *origin, buff[13]; + const char *origin; + char buff[13]; mp_set_mpe(pgfailback); mp_set_ovr(pgfailback); @@ -164,18 +182,19 @@ int select_pgfailback(struct config *conf, struct multipath * mp) mp_set_conf(pgfailback); mp_set_default(pgfailback, DEFAULT_FAILBACK); out: - print_pgfailback(buff, 13, &mp->pgfailback); + print_pgfailback(buff, 13, mp->pgfailback); condlog(3, "%s: failback = %s %s", mp->alias, buff, origin); return 0; } int select_pgpolicy(struct config *conf, struct multipath * mp) { - char *origin, buff[POLICY_NAME_SIZE]; + const char *origin; + char buff[POLICY_NAME_SIZE]; if (conf->pgpolicy_flag > 0) { mp->pgpolicy = conf->pgpolicy_flag; - origin = "(setting: multipath command line [-p] flag)"; + origin = cmdline_origin; goto out; } mp_set_mpe(pgpolicy); @@ -192,7 +211,7 @@ out: int select_selector(struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; mp_set_mpe(selector); mp_set_ovr(selector); @@ -209,7 +228,7 @@ out: static void select_alias_prefix (struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; mp_set_ovr(alias_prefix); mp_set_hwe(alias_prefix); @@ -224,17 +243,17 @@ static int want_user_friendly_names(struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; int user_friendly_names; do_set(user_friendly_names, mp->mpe, user_friendly_names, - "(setting: multipath.conf multipaths section)"); + multipaths_origin); do_set(user_friendly_names, conf->overrides, user_friendly_names, - "(setting: multipath.conf overrides section)"); + overrides_origin); do_set(user_friendly_names, mp->hwe, user_friendly_names, - "(setting: storage device configuration)"); + hwe_origin); do_set(user_friendly_names, conf, user_friendly_names, - "(setting: multipath.conf defaults/devices section)"); + conf_origin); do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES); out: condlog(3, "%s: user_friendly_names = %s %s", mp->wwid, @@ -245,11 +264,11 @@ out: int select_alias(struct config *conf, struct multipath * mp) { - char *origin = NULL; + const char *origin = NULL; if (mp->mpe && mp->mpe->alias) { mp->alias = STRDUP(mp->mpe->alias); - origin = "(setting: multipath.conf multipaths section)"; + origin = multipaths_origin; goto out; } @@ -309,14 +328,14 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa if (*no_path_retry == NO_PATH_RETRY_UNDEF) { *no_path_retry = NO_PATH_RETRY_QUEUE; print_no_path_retry(buff, sizeof(buff), - no_path_retry); + *no_path_retry); condlog(3, "%s: no_path_retry = %s (inherited setting from feature '%s')", id, buff, q_i_n_p); }; /* Warn only if features string is overridden */ if (*no_path_retry != NO_PATH_RETRY_QUEUE) { print_no_path_retry(buff, sizeof(buff), - no_path_retry); + *no_path_retry); condlog(2, "%s: ignoring feature '%s' because no_path_retry is set to '%s'", id, q_i_n_p, buff); } @@ -338,7 +357,7 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa int select_features(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; mp_set_mpe(features); mp_set_ovr(features); @@ -372,16 +391,20 @@ static int get_dh_state(struct path *pp, char *value, size_t value_len) int select_hwhandler(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; struct path *pp; /* dh_state is no longer than "detached" */ char handler[12]; + static char alua_name[] = "1 alua"; + static const char tpgs_origin[]= "(setting: autodetected from TPGS)"; char *dh_state; int i; + bool all_tpgs = true; dh_state = &handler[2]; if (mp->retain_hwhandler != RETAIN_HWHANDLER_OFF) { vector_foreach_slot(mp->paths, pp, i) { + all_tpgs = all_tpgs && (pp->tpgs > 0); if (get_dh_state(pp, dh_state, sizeof(handler) - 2) > 0 && strcmp(dh_state, "detached")) { memcpy(handler, "1 ", 2); @@ -396,6 +419,14 @@ int select_hwhandler(struct config *conf, struct multipath *mp) mp_set_conf(hwhandler); mp_set_default(hwhandler, DEFAULT_HWHANDLER); out: + if (all_tpgs && !strcmp(mp->hwhandler, DEFAULT_HWHANDLER) && + origin == default_origin) { + mp->hwhandler = alua_name; + origin = tpgs_origin; + } else if (!all_tpgs && !strcmp(mp->hwhandler, alua_name)) { + mp->hwhandler = DEFAULT_HWHANDLER; + origin = tpgs_origin; + } mp->hwhandler = STRDUP(mp->hwhandler); condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, origin); @@ -425,11 +456,12 @@ check_rdac(struct path * pp) int select_checker(struct config *conf, struct path *pp) { - char *origin, *checker_name; + const char *origin; + char *checker_name; struct checker * c = &pp->checker; if (pp->detect_checker == DETECT_CHECKER_ON) { - origin = "(setting: storage device autodetected)"; + origin = autodetect_origin; if (check_rdac(pp)) { checker_name = RDAC; goto out; @@ -438,32 +470,32 @@ int select_checker(struct config *conf, struct path *pp) goto out; } } - do_set(checker_name, conf->overrides, checker_name, "(setting: multipath.conf overrides section)"); - do_set(checker_name, pp->hwe, checker_name, "(setting: storage device configuration)"); - do_set(checker_name, conf, checker_name, "(setting: multipath.conf defaults/devices section)"); + do_set(checker_name, conf->overrides, checker_name, overrides_origin); + do_set(checker_name, pp->hwe, checker_name, hwe_origin); + do_set(checker_name, conf, checker_name, conf_origin); do_default(checker_name, DEFAULT_CHECKER); out: checker_get(conf->multipath_dir, c, checker_name); condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin); if (conf->checker_timeout) { c->timeout = conf->checker_timeout; - condlog(3, "%s: checker timeout = %u s (setting: multipath.conf defaults/devices section)", - pp->dev, c->timeout); + condlog(3, "%s: checker timeout = %u s %s", + pp->dev, c->timeout, conf_origin); } else if (sysfs_get_timeout(pp, &c->timeout) > 0) condlog(3, "%s: checker timeout = %u s (setting: kernel sysfs)", - pp->dev, c->timeout); + pp->dev, c->timeout); else { c->timeout = DEF_TIMEOUT; - condlog(3, "%s: checker timeout = %u s (setting: multipath internal)", - pp->dev, c->timeout); + condlog(3, "%s: checker timeout = %u s %s", + pp->dev, c->timeout, default_origin); } return 0; } int select_getuid(struct config *conf, struct path *pp) { - char *origin; + const char *origin; pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev); if (pp->uid_attribute) { @@ -515,24 +547,24 @@ do { \ int select_prio(struct config *conf, struct path *pp) { - char *origin; + const char *origin; struct mpentry * mpe; struct prio * p = &pp->prio; if (pp->detect_prio == DETECT_PRIO_ON) { detect_prio(conf, pp); if (prio_selected(p)) { - origin = "(setting: storage device autodetected)"; + origin = autodetect_origin; goto out; } } mpe = find_mpe(conf->mptable, pp->wwid); - set_prio(conf->multipath_dir, mpe, "(setting: multipath.conf multipaths section)"); - set_prio(conf->multipath_dir, conf->overrides, "(setting: multipath.conf overrides section)"); - set_prio(conf->multipath_dir, pp->hwe, "(setting: storage device configuration)"); - set_prio(conf->multipath_dir, conf, "(setting: multipath.conf defaults/devices section)"); + set_prio(conf->multipath_dir, mpe, multipaths_origin); + set_prio(conf->multipath_dir, conf->overrides, overrides_origin); + set_prio(conf->multipath_dir, pp->hwe, hwe_origin); + set_prio(conf->multipath_dir, conf, conf_origin); prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS); - origin = "(setting: multipath internal)"; + origin = default_origin; out: /* * fetch tpgs mode for alua, if its not already obtained @@ -552,7 +584,7 @@ out: int select_no_path_retry(struct config *conf, struct multipath *mp) { - char *origin = NULL; + const char *origin = NULL; char buff[12]; if (mp->disable_queueing) { @@ -565,25 +597,25 @@ int select_no_path_retry(struct config *conf, struct multipath *mp) mp_set_hwe(no_path_retry); mp_set_conf(no_path_retry); out: - print_no_path_retry(buff, 12, &mp->no_path_retry); + print_no_path_retry(buff, 12, mp->no_path_retry); if (origin) condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff, origin); else - condlog(3, "%s: no_path_retry = undef (setting: multipath internal)", - mp->alias); + condlog(3, "%s: no_path_retry = undef %s", + mp->alias, default_origin); return 0; } int select_minio_rq (struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; - do_set(minio_rq, mp->mpe, mp->minio, "(setting: multipath.conf multipaths section)"); - do_set(minio_rq, conf->overrides, mp->minio, "(setting: multipath.conf overrides section)"); - do_set(minio_rq, mp->hwe, mp->minio, "(setting: storage device configuration)"); - do_set(minio_rq, conf, mp->minio, "(setting: multipath.conf defaults/devices section)"); + do_set(minio_rq, mp->mpe, mp->minio, multipaths_origin); + do_set(minio_rq, conf->overrides, mp->minio, overrides_origin); + do_set(minio_rq, mp->hwe, mp->minio, hwe_origin); + do_set(minio_rq, conf, mp->minio, conf_origin); do_default(mp->minio, DEFAULT_MINIO_RQ); out: condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); @@ -593,7 +625,7 @@ out: int select_minio_bio (struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; mp_set_mpe(minio); mp_set_ovr(minio); @@ -617,21 +649,23 @@ int select_minio(struct config *conf, struct multipath *mp) int select_fast_io_fail(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_ovr(fast_io_fail); mp_set_hwe(fast_io_fail); mp_set_conf(fast_io_fail); mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); out: - print_fast_io_fail(buff, 12, &mp->fast_io_fail); + print_fast_io_fail(buff, 12, mp->fast_io_fail); condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); return 0; } int select_dev_loss(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_ovr(dev_loss); mp_set_hwe(dev_loss); @@ -639,14 +673,14 @@ int select_dev_loss(struct config *conf, struct multipath *mp) mp->dev_loss = 0; return 0; out: - print_dev_loss(buff, 12, &mp->dev_loss); + print_dev_loss(buff, 12, mp->dev_loss); condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin); return 0; } int select_flush_on_last_del(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; mp_set_mpe(flush_on_last_del); mp_set_ovr(flush_on_last_del); @@ -661,12 +695,13 @@ out: int select_reservation_key(struct config *conf, struct multipath *mp) { - char *origin, buff[PRKEY_SIZE]; + const char *origin; + char buff[PRKEY_SIZE]; char *from_file = ""; uint64_t prkey = 0; - do_prkey_set(mp->mpe, "(setting: multipath.conf multipaths section)"); - do_prkey_set(conf, "(setting: multipath.conf defaults/devices section)"); + do_prkey_set(mp->mpe, multipaths_origin); + do_prkey_set(conf, conf_origin); put_be64(mp->reservation_key, 0); mp->prkey_source = PRKEY_SOURCE_NONE; return 0; @@ -687,7 +722,7 @@ out: int select_retain_hwhandler(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; unsigned int minv_dm_retain[3] = {1, 5, 0}; if (!VERSION_GE(conf->version, minv_dm_retain)) { @@ -713,7 +748,7 @@ out: int select_detect_prio(struct config *conf, struct path *pp) { - char *origin; + const char *origin; pp_set_ovr(detect_prio); pp_set_hwe(detect_prio); @@ -727,7 +762,7 @@ out: int select_detect_checker(struct config *conf, struct path *pp) { - char *origin; + const char *origin; pp_set_ovr(detect_checker); pp_set_hwe(detect_checker); @@ -742,7 +777,7 @@ out: int select_deferred_remove(struct config *conf, struct multipath *mp) { - char *origin; + const char *origin; #ifndef LIBDM_API_DEFERRED mp->deferred_remove = DEFERRED_REMOVE_OFF; @@ -767,7 +802,8 @@ out: int select_delay_watch_checks(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(delay_watch_checks); mp_set_ovr(delay_watch_checks); @@ -775,14 +811,15 @@ int select_delay_watch_checks(struct config *conf, struct multipath *mp) mp_set_conf(delay_watch_checks); mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS); out: - print_off_int_undef(buff, 12, &mp->delay_watch_checks); + print_off_int_undef(buff, 12, mp->delay_watch_checks); condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin); return 0; } int select_delay_wait_checks(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(delay_wait_checks); mp_set_ovr(delay_wait_checks); @@ -790,7 +827,7 @@ int select_delay_wait_checks(struct config *conf, struct multipath *mp) mp_set_conf(delay_wait_checks); mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS); out: - print_off_int_undef(buff, 12, &mp->delay_wait_checks); + print_off_int_undef(buff, 12, mp->delay_wait_checks); condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin); return 0; @@ -798,7 +835,8 @@ out: int select_marginal_path_err_sample_time(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(marginal_path_err_sample_time); mp_set_ovr(marginal_path_err_sample_time); @@ -806,7 +844,7 @@ int select_marginal_path_err_sample_time(struct config *conf, struct multipath * mp_set_conf(marginal_path_err_sample_time); mp_set_default(marginal_path_err_sample_time, DEFAULT_ERR_CHECKS); out: - print_off_int_undef(buff, 12, &mp->marginal_path_err_sample_time); + print_off_int_undef(buff, 12, mp->marginal_path_err_sample_time); condlog(3, "%s: marginal_path_err_sample_time = %s %s", mp->alias, buff, origin); return 0; @@ -814,7 +852,8 @@ out: int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(marginal_path_err_rate_threshold); mp_set_ovr(marginal_path_err_rate_threshold); @@ -822,7 +861,7 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat mp_set_conf(marginal_path_err_rate_threshold); mp_set_default(marginal_path_err_rate_threshold, DEFAULT_ERR_CHECKS); out: - print_off_int_undef(buff, 12, &mp->marginal_path_err_rate_threshold); + print_off_int_undef(buff, 12, mp->marginal_path_err_rate_threshold); condlog(3, "%s: marginal_path_err_rate_threshold = %s %s", mp->alias, buff, origin); return 0; @@ -830,7 +869,8 @@ out: int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(marginal_path_err_recheck_gap_time); mp_set_ovr(marginal_path_err_recheck_gap_time); @@ -838,7 +878,7 @@ int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multip mp_set_conf(marginal_path_err_recheck_gap_time); mp_set_default(marginal_path_err_recheck_gap_time, DEFAULT_ERR_CHECKS); out: - print_off_int_undef(buff, 12, &mp->marginal_path_err_recheck_gap_time); + print_off_int_undef(buff, 12, mp->marginal_path_err_recheck_gap_time); condlog(3, "%s: marginal_path_err_recheck_gap_time = %s %s", mp->alias, buff, origin); return 0; @@ -846,7 +886,8 @@ out: int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(marginal_path_double_failed_time); mp_set_ovr(marginal_path_double_failed_time); @@ -854,7 +895,7 @@ int select_marginal_path_double_failed_time(struct config *conf, struct multipat mp_set_conf(marginal_path_double_failed_time); mp_set_default(marginal_path_double_failed_time, DEFAULT_ERR_CHECKS); out: - print_off_int_undef(buff, 12, &mp->marginal_path_double_failed_time); + print_off_int_undef(buff, 12, mp->marginal_path_double_failed_time); condlog(3, "%s: marginal_path_double_failed_time = %s %s", mp->alias, buff, origin); return 0; @@ -862,7 +903,7 @@ out: int select_skip_kpartx (struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; mp_set_mpe(skip_kpartx); mp_set_ovr(skip_kpartx); @@ -878,7 +919,7 @@ out: int select_max_sectors_kb(struct config *conf, struct multipath * mp) { - char *origin; + const char *origin; mp_set_mpe(max_sectors_kb); mp_set_ovr(max_sectors_kb); @@ -899,7 +940,8 @@ out: int select_ghost_delay (struct config *conf, struct multipath * mp) { - char *origin, buff[12]; + const char *origin; + char buff[12]; mp_set_mpe(ghost_delay); mp_set_ovr(ghost_delay); @@ -907,7 +949,32 @@ int select_ghost_delay (struct config *conf, struct multipath * mp) mp_set_conf(ghost_delay); mp_set_default(ghost_delay, DEFAULT_GHOST_DELAY); out: - print_off_int_undef(buff, 12, &mp->ghost_delay); + print_off_int_undef(buff, 12, mp->ghost_delay); condlog(3, "%s: ghost_delay = %s %s", mp->alias, buff, origin); return 0; } + +int select_find_multipaths_timeout(struct config *conf, struct path *pp) +{ + const char *origin; + + pp_set_conf(find_multipaths_timeout); + pp_set_default(find_multipaths_timeout, + DEFAULT_FIND_MULTIPATHS_TIMEOUT); +out: + /* + * If configured value is negative, and this "unknown" hardware + * (no hwentry), use very small timeout to avoid delays. + */ + if (pp->find_multipaths_timeout < 0) { + pp->find_multipaths_timeout = -pp->find_multipaths_timeout; + if (!pp->hwe) { + pp->find_multipaths_timeout = + DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT; + origin = "(default for unknown hardware)"; + } + } + condlog(3, "%s: timeout for find_multipaths \"smart\" = %ds %s", + pp->dev, pp->find_multipaths_timeout, origin); + return 0; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 136f906..a022bee 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -8,6 +8,7 @@ int select_hwhandler (struct config *conf, struct multipath * mp); int select_checker(struct config *conf, struct path *pp); int select_getuid (struct config *conf, struct path * pp); int select_prio (struct config *conf, struct path * pp); +int select_find_multipaths_timeout(struct config *conf, struct path *pp); int select_no_path_retry(struct config *conf, struct multipath *mp); int select_flush_on_last_del(struct config *conf, struct multipath *mp); int select_minio(struct config *conf, struct multipath *mp); diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 88a4b78..eb6a178 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -102,6 +102,32 @@ enum yes_no_undef_states { YNU_YES, }; +#define _FIND_MULTIPATHS_F (1 << 1) +#define _FIND_MULTIPATHS_I (1 << 2) +#define _FIND_MULTIPATHS_N (1 << 3) +/* + * _FIND_MULTIPATHS_F must have the same value as YNU_YES. + * Generate a compile time error if that isn't the case. + */ +char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)]; + +#define find_multipaths_on(conf) \ + (!!((conf)->find_multipaths & _FIND_MULTIPATHS_F)) +#define ignore_wwids_on(conf) \ + (!!((conf)->find_multipaths & _FIND_MULTIPATHS_I)) +#define ignore_new_devs_on(conf) \ + (!!((conf)->find_multipaths & _FIND_MULTIPATHS_N)) + +enum find_multipaths_states { + FIND_MULTIPATHS_UNDEF = YNU_UNDEF, + FIND_MULTIPATHS_OFF = YNU_NO, + FIND_MULTIPATHS_ON = _FIND_MULTIPATHS_F, + FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N, + FIND_MULTIPATHS_GREEDY = _FIND_MULTIPATHS_I, + FIND_MULTIPATHS_SMART = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_I, + __FIND_MULTIPATHS_LAST, +}; + enum flush_states { FLUSH_UNDEF = YNU_UNDEF, FLUSH_DISABLED = YNU_NO, @@ -255,6 +281,7 @@ struct path { int io_err_disable_reinstate; int io_err_pathfail_cnt; int io_err_pathfail_starttime; + int find_multipaths_timeout; /* configlet pointers */ struct hwentry * hwe; struct gen_path generic_path; @@ -296,6 +323,7 @@ struct multipath { int max_sectors_kb; int force_readonly; int force_udev_reload; + int needs_paths_uevent; int ghost_delay; int ghost_delay_tick; unsigned int dev_loss; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index f9dc8a8..38f0438 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -10,7 +10,6 @@ #include "structs.h" #include "structs_vec.h" #include "sysfs.h" -#include "waiter.h" #include "devmapper.h" #include "dmparser.h" #include "propsel.h" @@ -72,9 +71,10 @@ int adopt_paths(vector pathvec, struct multipath *mpp) store_path(mpp->paths, pp)) return 1; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = pathinfo(pp, conf, DI_PRIO | DI_CHECKER); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (ret) return 1; } @@ -108,33 +108,13 @@ void orphan_paths(vector pathvec, struct multipath *mpp) } } -static void -set_multipath_wwid (struct multipath * mpp) -{ - if (strlen(mpp->wwid)) - return; - - dm_get_uuid(mpp->alias, mpp->wwid); -} - -#define KEEP_WAITER 0 -#define STOP_WAITER 1 -#define PURGE_VEC 1 - -static void -_remove_map (struct multipath * mpp, struct vectors * vecs, - int stop_waiter, int purge_vec) +void +remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec) { int i; condlog(4, "%s: remove multipath map", mpp->alias); - /* - * stop the DM event waiter thread - */ - if (stop_waiter) - stop_waiter_thread(mpp, vecs); - /* * clear references to this map */ @@ -150,19 +130,16 @@ _remove_map (struct multipath * mpp, struct vectors * vecs, free_multipath(mpp, KEEP_PATHS); } -void remove_map(struct multipath *mpp, struct vectors *vecs, int purge_vec) -{ - _remove_map(mpp, vecs, KEEP_WAITER, purge_vec); -} - -void remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs, - int purge_vec) +void +remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec) { - _remove_map(mpp, vecs, STOP_WAITER, purge_vec); + struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias); + if (mpp) + remove_map(mpp, vecs, purge_vec); } -static void -_remove_maps (struct vectors * vecs, int stop_waiter) +void +remove_maps(struct vectors * vecs) { int i; struct multipath * mpp; @@ -171,7 +148,7 @@ _remove_maps (struct vectors * vecs, int stop_waiter) return; vector_foreach_slot (vecs->mpvec, mpp, i) { - _remove_map(mpp, vecs, stop_waiter, 1); + remove_map(mpp, vecs, 1); i--; } @@ -179,16 +156,6 @@ _remove_maps (struct vectors * vecs, int stop_waiter) vecs->mpvec = NULL; } -void remove_maps(struct vectors *vecs) -{ - _remove_maps(vecs, KEEP_WAITER); -} - -void remove_maps_and_stop_waiters(struct vectors *vecs) -{ - _remove_maps(vecs, STOP_WAITER); -} - void extract_hwe_from_path(struct multipath * mpp) { @@ -310,7 +277,10 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) void enter_recovery_mode(struct multipath *mpp) { + int checkint; struct config *conf = get_multipath_config(); + checkint = conf->checkint; + put_multipath_config(conf); /* * Enter retry mode. @@ -318,65 +288,9 @@ void enter_recovery_mode(struct multipath *mpp) * starting retry. */ mpp->stat_queueing_timeouts++; - mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; + mpp->retry_tick = mpp->no_path_retry * checkint + 1; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); - put_multipath_config(conf); -} - -static void set_no_path_retry(struct multipath *mpp) -{ - char is_queueing = 0; - - mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); - if (mpp->features && strstr(mpp->features, "queue_if_no_path")) - is_queueing = 1; - - switch (mpp->no_path_retry) { - case NO_PATH_RETRY_UNDEF: - break; - case NO_PATH_RETRY_FAIL: - if (is_queueing) - dm_queue_if_no_path(mpp->alias, 0); - break; - case NO_PATH_RETRY_QUEUE: - if (!is_queueing) - dm_queue_if_no_path(mpp->alias, 1); - break; - default: - if (mpp->nr_active > 0) { - mpp->retry_tick = 0; - dm_queue_if_no_path(mpp->alias, 1); - } else if (is_queueing && mpp->retry_tick == 0) - enter_recovery_mode(mpp); - break; - } -} - -int __setup_multipath(struct vectors *vecs, struct multipath *mpp, - int reset) -{ - if (dm_get_info(mpp->alias, &mpp->dmi)) { - /* Error accessing table */ - condlog(3, "%s: cannot access table", mpp->alias); - goto out; - } - - if (update_multipath_strings(mpp, vecs->pathvec, 1)) { - condlog(0, "%s: failed to setup multipath", mpp->alias); - goto out; - } - - if (reset) { - set_no_path_retry(mpp); - if (VECTOR_SIZE(mpp->paths) != 0) - dm_cancel_deferred_remove(mpp); - } - - return 0; -out: - remove_map(mpp, vecs, PURGE_VEC); - return 1; } void @@ -410,92 +324,6 @@ sync_map_state(struct multipath *mpp) } } -int -update_map (struct multipath *mpp, struct vectors *vecs) -{ - int retries = 3; - char params[PARAMS_SIZE] = {0}; - -retry: - condlog(4, "%s: updating new map", mpp->alias); - if (adopt_paths(vecs->pathvec, mpp)) { - condlog(0, "%s: failed to adopt paths for new map update", - mpp->alias); - retries = -1; - goto fail; - } - verify_paths(mpp, vecs); - mpp->action = ACT_RELOAD; - - extract_hwe_from_path(mpp); - if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { - condlog(0, "%s: failed to setup new map in update", mpp->alias); - retries = -1; - goto fail; - } - if (domap(mpp, params, 1) <= 0 && retries-- > 0) { - condlog(0, "%s: map_udate sleep", mpp->alias); - sleep(1); - goto retry; - } - dm_lib_release(); - -fail: - if (setup_multipath(vecs, mpp)) - return 1; - - sync_map_state(mpp); - - if (retries < 0) - condlog(0, "%s: failed reload in new map update", mpp->alias); - return 0; -} - -struct multipath *add_map_without_path (struct vectors *vecs, const char *alias) -{ - struct multipath * mpp = alloc_multipath(); - struct config *conf; - - if (!mpp) - return NULL; - if (!alias) { - FREE(mpp); - return NULL; - } - - mpp->alias = STRDUP(alias); - - if (dm_get_info(mpp->alias, &mpp->dmi)) { - condlog(3, "%s: cannot access table", mpp->alias); - goto out; - } - set_multipath_wwid(mpp); - conf = get_multipath_config(); - mpp->mpe = find_mpe(conf->mptable, mpp->wwid); - put_multipath_config(conf); - - if (update_multipath_table(mpp, vecs->pathvec, 1)) - goto out; - if (update_multipath_status(mpp)) - goto out; - - if (!vector_alloc_slot(vecs->mpvec)) - goto out; - - vector_set_slot(vecs->mpvec, mpp); - - if (update_map(mpp, vecs) != 0) /* map removed */ - return NULL; - - if (start_waiter_thread(mpp, vecs)) - goto out; - - return mpp; -out: - remove_map(mpp, vecs, PURGE_VEC); - return NULL; -} - static void find_existing_alias (struct multipath * mpp, struct vectors *vecs) @@ -588,54 +416,6 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs) return count; } -int update_multipath (struct vectors *vecs, char *mapname, int reset) -{ - struct multipath *mpp; - struct pathgroup *pgp; - struct path *pp; - int i, j; - - mpp = find_mp_by_alias(vecs->mpvec, mapname); - - if (!mpp) { - condlog(3, "%s: multipath map not found", mapname); - return 2; - } - - if (__setup_multipath(vecs, mpp, reset)) - return 1; /* mpp freed in setup_multipath */ - - /* - * compare checkers states with DM states - */ - vector_foreach_slot (mpp->pg, pgp, i) { - vector_foreach_slot (pgp->paths, pp, j) { - if (pp->dmstate != PSTATE_FAILED) - continue; - - if (pp->state != PATH_DOWN) { - struct config *conf = get_multipath_config(); - int oldstate = pp->state; - condlog(2, "%s: mark as failed", pp->dev); - mpp->stat_path_failures++; - pp->state = PATH_DOWN; - if (oldstate == PATH_UP || - oldstate == PATH_GHOST) - update_queue_mode_del_path(mpp); - - /* - * if opportune, - * schedule the next check earlier - */ - if (pp->tick > conf->checkint) - pp->tick = conf->checkint; - put_multipath_config(conf); - } - } - } - return 0; -} - /* * mpp->no_path_retry: * -2 (QUEUE) : queue_if_no_path enabled, never turned off diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 3749eb6..4220ea3 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -19,24 +19,20 @@ void orphan_path (struct path * pp, const char *reason); int verify_paths(struct multipath * mpp, struct vectors * vecs); int update_mpp_paths(struct multipath * mpp, vector pathvec); -int __setup_multipath (struct vectors * vecs, struct multipath * mpp, - int reset); -#define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1) int update_multipath_strings (struct multipath *mpp, vector pathvec, int is_daemon); void extract_hwe_from_path(struct multipath * mpp); +#define PURGE_VEC 1 + void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec); -void remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, int purge_vec); +void remove_map_by_alias(const char *alias, struct vectors * vecs, + int purge_vec); void remove_maps (struct vectors * vecs); -void remove_maps_and_stop_waiters (struct vectors * vecs); void sync_map_state (struct multipath *); -int update_map (struct multipath *mpp, struct vectors *vecs); -struct multipath * add_map_without_path (struct vectors * vecs, const char * alias); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); -int update_multipath (struct vectors *vecs, char *mapname, int reset); void update_queue_mode_del_path(struct multipath *mpp); void update_queue_mode_add_path(struct multipath *mpp); int update_multipath_table (struct multipath *mpp, vector pathvec, diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index 97e0997..ee72e6a 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -287,3 +288,68 @@ int sysfs_check_holders(char * check_devt, char * new_devt) return 0; } + +static int select_dm_devs(const struct dirent *di) +{ + return fnmatch("dm-*", di->d_name, FNM_FILE_NAME) == 0; +} + +static void close_fd(void *arg) +{ + close((long)arg); +} + +bool sysfs_is_multipathed(const struct path *pp) +{ + char pathbuf[PATH_MAX]; + struct dirent **di; + int n, r, i; + bool found = false; + + n = snprintf(pathbuf, sizeof(pathbuf), "/sys/block/%s/holders", + pp->dev); + + if (n >= sizeof(pathbuf)) { + condlog(1, "%s: pathname overflow", __func__); + return false; + } + + r = scandir(pathbuf, &di, select_dm_devs, alphasort); + if (r == 0) + return false; + else if (r < 0) { + condlog(1, "%s: error scanning %s", __func__, pathbuf); + return false; + } + + pthread_cleanup_push(free, di); + for (i = 0; i < r && !found; i++) { + long fd; + int nr; + char uuid[6]; + + if (snprintf(pathbuf + n, sizeof(pathbuf) - n, + "/%s/dm/uuid", di[i]->d_name) + >= sizeof(pathbuf) - n) + continue; + + fd = open(pathbuf, O_RDONLY); + if (fd == -1) { + condlog(1, "%s: error opening %s", __func__, pathbuf); + continue; + } + + pthread_cleanup_push(close_fd, (void *)fd); + nr = read(fd, uuid, sizeof(uuid)); + if (nr == sizeof(uuid) && !memcmp(uuid, "mpath-", sizeof(uuid))) + found = true; + else if (nr < 0) { + condlog(1, "%s: error reading from %s: %s", + __func__, pathbuf, strerror(errno)); + } + pthread_cleanup_pop(1); + } + pthread_cleanup_pop(1); + + return found; +} diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h index 75c0f9c..9ae30b3 100644 --- a/libmultipath/sysfs.h +++ b/libmultipath/sysfs.h @@ -4,6 +4,7 @@ #ifndef _LIBMULTIPATH_SYSFS_H #define _LIBMULTIPATH_SYSFS_H +#include ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, const char * value, size_t value_len); @@ -13,4 +14,5 @@ ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, unsigned char * value, size_t value_len); int sysfs_get_size (struct path *pp, unsigned long long * size); int sysfs_check_holders(char * check_devt, char * new_devt); +bool sysfs_is_multipathed(const struct path *pp); #endif diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index c6a9e8b..fd8ca35 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -162,8 +162,9 @@ uevent_get_wwid(struct uevent *uev) struct config * conf; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel); - put_multipath_config(conf); + pthread_cleanup_pop(1); val = uevent_get_env_var(uev, uid_attribute); if (val) @@ -188,6 +189,7 @@ uevent_need_merge(void) bool uevent_can_discard(struct uevent *uev) { + int invalid = 0; struct config * conf; /* @@ -199,13 +201,14 @@ uevent_can_discard(struct uevent *uev) * filter paths devices by devnode */ conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, - uev->kernel) > 0) { - put_multipath_config(conf); - return true; - } - put_multipath_config(conf); + uev->kernel) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) + return true; return false; } @@ -330,7 +333,7 @@ uevent_filter(struct uevent *later, struct list_head *tmpq) * by the later uevent */ if (uevent_can_filter(earlier, later)) { - condlog(2, "uevent: %s-%s has filtered by uevent: %s-%s", + condlog(3, "uevent: %s-%s has filtered by uevent: %s-%s", earlier->kernel, earlier->action, later->kernel, later->action); @@ -354,7 +357,7 @@ uevent_merge(struct uevent *later, struct list_head *tmpq) * merge earlier uevents to the later uevent */ if (uevent_can_merge(earlier, later)) { - condlog(2, "merged uevent: %s-%s-%s with uevent: %s-%s-%s", + condlog(3, "merged uevent: %s-%s-%s with uevent: %s-%s-%s", earlier->action, earlier->kernel, earlier->wwid, later->action, later->kernel, later->wwid); diff --git a/libmultipath/util.c b/libmultipath/util.c index d3dd3eb..7251ad0 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -30,30 +30,21 @@ strchop(char *str) } int -basenamecpy (const char * str1, char * str2, int str2len) +basenamecpy (const char *src, char *dst, size_t size) { - const char *p; + const char *p, *e; - if (!str1 || !strlen(str1)) + if (!src || !dst || !strlen(src)) return 0; - if (strlen(str1) >= str2len) - return 0; + p = basename(src); - if (!str2) + for (e = p + strlen(p) - 1; e >= p && isspace(*e); --e) ; + if (e < p || e - p > size - 2) return 0; - p = str1 + (strlen(str1) - 1); - - while (*--p != '/' && p != str1) - continue; - - if (p != str1) - p++; - - strncpy(str2, p, str2len); - str2[str2len - 1] = '\0'; - return strchop(str2); + strlcpy(dst, p, e - p + 2); + return strlen(dst); } int diff --git a/libmultipath/util.h b/libmultipath/util.h index 51a6d54..a3ab894 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -5,7 +5,7 @@ #include size_t strchop(char *); -int basenamecpy (const char * src, char * dst, int); +int basenamecpy (const char *src, char *dst, size_t size); int filepresent (char * run); char *get_next_string(char **temp, char *split_char); int get_word (char * sentence, char ** word); diff --git a/libmultipath/vector.c b/libmultipath/vector.c index 6266e0a..f741ae0 100644 --- a/libmultipath/vector.c +++ b/libmultipath/vector.c @@ -145,18 +145,26 @@ vector_repack(vector v) vector_del_slot(v, i--); } -/* Free memory vector allocation */ -void -vector_free(vector v) +vector +vector_reset(vector v) { if (!v) - return; + return NULL; if (v->slot) FREE(v->slot); v->allocated = 0; v->slot = NULL; + return v; +} + +/* Free memory vector allocation */ +void +vector_free(vector v) +{ + if (!vector_reset(v)) + return; FREE(v); } diff --git a/libmultipath/vector.h b/libmultipath/vector.h index 6018b57..b9450ac 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -74,6 +74,7 @@ typedef struct _vector *vector; /* Prototypes */ extern vector vector_alloc(void); extern void *vector_alloc_slot(vector v); +vector vector_reset(vector v); extern void vector_free(vector v); #define vector_free_const(x) vector_free((vector)(long)(x)) extern void free_strvec(vector strvec); diff --git a/libmultipath/version.h b/libmultipath/version.h index f25df1c..25dad1d 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000706 -#define DATE_CODE 0x030a12 +#define VERSION_CODE 0x000707 +#define DATE_CODE 0x050b12 #define PROG "multipath-tools" diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c deleted file mode 100644 index cb9708b..0000000 --- a/libmultipath/waiter.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2004, 2005 Christophe Varoqui - * Copyright (c) 2005 Kiyoshi Ueda, NEC - * Copyright (c) 2005 Benjamin Marzinski, Redhat - * Copyright (c) 2005 Edward Goggin, EMC - */ -#include -#include -#include -#include -#include -#include - -#include "vector.h" -#include "memory.h" -#include "checkers.h" -#include "config.h" -#include "structs.h" -#include "structs_vec.h" -#include "devmapper.h" -#include "debug.h" -#include "lock.h" -#include "waiter.h" - -pthread_attr_t waiter_attr; - -static struct event_thread *alloc_waiter (void) -{ - - struct event_thread *wp; - - wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); - memset(wp, 0, sizeof(struct event_thread)); - - return wp; -} - -static void free_waiter (void *data) -{ - struct event_thread *wp = (struct event_thread *)data; - - if (wp->dmt) - dm_task_destroy(wp->dmt); - - rcu_unregister_thread(); - FREE(wp); -} - -void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs) -{ - pthread_t thread; - - if (mpp->waiter == (pthread_t)0) { - condlog(3, "%s: event checker thread already stopped", - mpp->alias); - return; - } - condlog(2, "%s: stop event checker thread (%lu)", mpp->alias, - mpp->waiter); - thread = mpp->waiter; - mpp->waiter = (pthread_t)0; - pthread_cancel(thread); - pthread_kill(thread, SIGUSR2); -} - -/* - * returns the reschedule delay - * negative means *stop* - */ -static int waiteventloop (struct event_thread *waiter) -{ - sigset_t set, oldset; - int event_nr; - int r; - - if (!waiter->event_nr) - waiter->event_nr = dm_geteventnr(waiter->mapname); - - if (!(waiter->dmt = libmp_dm_task_create(DM_DEVICE_WAITEVENT))) { - condlog(0, "%s: devmap event #%i dm_task_create error", - waiter->mapname, waiter->event_nr); - return 1; - } - - if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { - condlog(0, "%s: devmap event #%i dm_task_set_name error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - waiter->dmt = NULL; - return 1; - } - - if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, - waiter->event_nr)) { - condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - waiter->dmt = NULL; - return 1; - } - - dm_task_no_open_count(waiter->dmt); - - /* wait */ - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - pthread_sigmask(SIG_UNBLOCK, &set, &oldset); - - pthread_testcancel(); - r = dm_task_run(waiter->dmt); - pthread_testcancel(); - - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - dm_task_destroy(waiter->dmt); - waiter->dmt = NULL; - - if (!r) /* wait interrupted by signal */ - return -1; - - waiter->event_nr++; - - /* - * upon event ... - */ - while (1) { - condlog(3, "%s: devmap event #%i", - waiter->mapname, waiter->event_nr); - - /* - * event might be : - * - * 1) a table reload, which means our mpp structure is - * obsolete : refresh it through update_multipath() - * 2) a path failed by DM : mark as such through - * update_multipath() - * 3) map has gone away : stop the thread. - * 4) a path reinstate : nothing to do - * 5) a switch group : nothing to do - */ - pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock); - lock(&waiter->vecs->lock); - pthread_testcancel(); - r = update_multipath(waiter->vecs, waiter->mapname, 1); - lock_cleanup_pop(waiter->vecs->lock); - - if (r) { - condlog(2, "%s: event checker exit", - waiter->mapname); - return -1; /* stop the thread */ - } - - event_nr = dm_geteventnr(waiter->mapname); - - if (waiter->event_nr == event_nr) - return 1; /* upon problem reschedule 1s later */ - - waiter->event_nr = event_nr; - } - return -1; /* never reach there */ -} - -static void *waitevent (void *et) -{ - int r; - struct event_thread *waiter; - - mlockall(MCL_CURRENT | MCL_FUTURE); - - waiter = (struct event_thread *)et; - pthread_cleanup_push(free_waiter, et); - - rcu_register_thread(); - while (1) { - r = waiteventloop(waiter); - - if (r < 0) - break; - - sleep(r); - } - - pthread_cleanup_pop(1); - return NULL; -} - -int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) -{ - struct event_thread *wp; - - if (!mpp) - return 0; - - wp = alloc_waiter(); - - if (!wp) - goto out; - - strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1); - wp->vecs = vecs; - - if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { - condlog(0, "%s: cannot create event checker", wp->mapname); - goto out1; - } - mpp->waiter = wp->thread; - condlog(2, "%s: event checker started", wp->mapname); - - return 0; -out1: - free_waiter(wp); - mpp->waiter = (pthread_t)0; -out: - condlog(0, "failed to start waiter thread"); - return 1; -} diff --git a/libmultipath/waiter.h b/libmultipath/waiter.h deleted file mode 100644 index 0cfae46..0000000 --- a/libmultipath/waiter.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _WAITER_H -#define _WAITER_H - -extern pthread_attr_t waiter_attr; - -struct event_thread { - struct dm_task *dmt; - pthread_t thread; - int event_nr; - char mapname[WWID_SIZE]; - struct vectors *vecs; -}; - -void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs); -int start_waiter_thread (struct multipath *mpp, struct vectors *vecs); - -#endif /* _WAITER_H */ diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c index bc70a27..53e7951 100644 --- a/libmultipath/wwids.c +++ b/libmultipath/wwids.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -15,6 +16,7 @@ #include "wwids.h" #include "defaults.h" #include "config.h" +#include "devmapper.h" /* * Copyright (c) 2010 Benjamin Marzinski, Redhat @@ -92,8 +94,9 @@ replace_wwids(vector mp) struct config *conf; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (fd < 0) goto out; if (!can_write) { @@ -206,8 +209,9 @@ remove_wwid(char *wwid) { } condlog(3, "removing line '%s' from wwids file", str); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (fd < 0) goto out; if (!can_write) { @@ -231,8 +235,9 @@ check_wwids_file(char *wwid, int write_wwid) struct config *conf; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (fd < 0) return -1; @@ -271,22 +276,30 @@ out: } int -should_multipath(struct path *pp1, vector pathvec) +should_multipath(struct path *pp1, vector pathvec, vector mpvec) { - int i, ignore_new_devs; + int i, ignore_new_devs, find_multipaths; struct path *pp2; struct config *conf; conf = get_multipath_config(); - ignore_new_devs = conf->ignore_new_devs; - if (!conf->find_multipaths && !ignore_new_devs) { - put_multipath_config(conf); - return 1; - } + ignore_new_devs = ignore_new_devs_on(conf); + find_multipaths = find_multipaths_on(conf); put_multipath_config(conf); + if (!find_multipaths && !ignore_new_devs) + return 1; condlog(4, "checking if %s should be multipathed", pp1->dev); if (!ignore_new_devs) { + char tmp_wwid[WWID_SIZE]; + struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid); + + if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid) == 0 && + !strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) { + condlog(3, "wwid %s is already multipathed, keeping it", + pp1->wwid); + return 1; + } vector_foreach_slot(pathvec, pp2, i) { if (pp1->dev == pp2->dev) continue; @@ -319,5 +332,114 @@ remember_wwid(char *wwid) condlog(3, "wrote wwid %s to wwids file", wwid); else condlog(4, "wwid %s already in wwids file", wwid); - return 0; + return ret; +} + +static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids"; +static const char shm_lock[] = ".lock"; +static const char shm_header[] = "multipath shm lock file, don't edit"; +static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)]; +static const char *shm_lock_path = &_shm_lock_path[0]; + +static void init_shm_paths(void) +{ + snprintf(_shm_lock_path, sizeof(_shm_lock_path), + "%s/%s", shm_dir, shm_lock); +} + +static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT; + +static int multipath_shm_open(bool rw) +{ + int fd; + int can_write; + + pthread_once(&shm_path_once, init_shm_paths); + fd = open_file(shm_lock_path, &can_write, shm_header); + + if (fd >= 0 && rw && !can_write) { + close(fd); + condlog(1, "failed to open %s for writing", shm_dir); + return -1; + } + + return fd; } + +static void multipath_shm_close(void *arg) +{ + long fd = (long)arg; + + close(fd); + unlink(shm_lock_path); +} + +static int _failed_wwid_op(const char *wwid, bool rw, + int (*func)(const char *), const char *msg) +{ + char path[PATH_MAX]; + long lockfd; + int r = -1; + + if (snprintf(path, sizeof(path), "%s/%s", shm_dir, wwid) + >= sizeof(path)) { + condlog(1, "%s: path name overflow", __func__); + return -1; + } + + lockfd = multipath_shm_open(rw); + if (lockfd == -1) + return -1; + + pthread_cleanup_push(multipath_shm_close, (void *)lockfd); + r = func(path); + pthread_cleanup_pop(1); + + if (r == WWID_FAILED_ERROR) + condlog(1, "%s: %s: %s", msg, wwid, strerror(errno)); + else if (r == WWID_FAILED_CHANGED) + condlog(3, "%s: %s", msg, wwid); + else if (!rw) + condlog(4, "%s: %s is %s", msg, wwid, + r == WWID_IS_FAILED ? "failed" : "good"); + + return r; +} + +static int _is_failed(const char *path) +{ + struct stat st; + + if (lstat(path, &st) == 0) + return WWID_IS_FAILED; + else if (errno == ENOENT) + return WWID_IS_NOT_FAILED; + else + return WWID_FAILED_ERROR; +} + +static int _mark_failed(const char *path) +{ + /* Called from _failed_wwid_op: we know that shm_lock_path exists */ + if (_is_failed(path) == WWID_IS_FAILED) + return WWID_FAILED_UNCHANGED; + return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED : + WWID_FAILED_ERROR); +} + +static int _unmark_failed(const char *path) +{ + if (_is_failed(path) == WWID_IS_NOT_FAILED) + return WWID_FAILED_UNCHANGED; + return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR); +} + +#define declare_failed_wwid_op(op, rw) \ +int op ## _wwid(const char *wwid) \ +{ \ + return _failed_wwid_op(wwid, (rw), _ ## op, #op); \ +} + +declare_failed_wwid_op(is_failed, false) +declare_failed_wwid_op(mark_failed, true) +declare_failed_wwid_op(unmark_failed, true) diff --git a/libmultipath/wwids.h b/libmultipath/wwids.h index 9527012..0c6ee54 100644 --- a/libmultipath/wwids.h +++ b/libmultipath/wwids.h @@ -12,10 +12,21 @@ "#\n" \ "# Valid WWIDs:\n" -int should_multipath(struct path *pp, vector pathvec); +int should_multipath(struct path *pp, vector pathvec, vector mpvec); int remember_wwid(char *wwid); int check_wwids_file(char *wwid, int write_wwid); int remove_wwid(char *wwid); int replace_wwids(vector mp); +enum { + WWID_IS_NOT_FAILED = 0, + WWID_IS_FAILED, + WWID_FAILED_UNCHANGED, + WWID_FAILED_CHANGED, + WWID_FAILED_ERROR = -1, +}; + +int is_failed_wwid(const char *wwid); +int mark_failed_wwid(const char *wwid); +int unmark_failed_wwid(const char *wwid); #endif /* _WWIDS_H */ diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 79b89e5..5b37f3a 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -11,6 +11,7 @@ #include #include "mpath_persist.h" #include "main.h" +#include "debug.h" #include #include #include @@ -47,7 +48,7 @@ struct config *get_multipath_config(void) return multipath_conf; } -void put_multipath_config(struct config *conf) +void put_multipath_config(void * arg) { /* Noop for now */ } diff --git a/multipath/11-dm-mpath.rules b/multipath/11-dm-mpath.rules index 03ac5da..07320a1 100644 --- a/multipath/11-dm-mpath.rules +++ b/multipath/11-dm-mpath.rules @@ -26,6 +26,10 @@ ENV{DM_NR_VALID_PATHS}=="0", ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action" ENV{MPATH_SBIN_PATH}="/sbin" TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" +# Don't run multipath -U during "coldplug" after switching root, +# because paths are just being added to the udev db. +ACTION=="add", ENV{.MPATH_DEVICE_READY_OLD}=="1", GOTO="paths_ok" + # Check the map state directly with multipath -U. # This doesn't attempt I/O on the device. PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U %k", GOTO="paths_ok" diff --git a/multipath/main.c b/multipath/main.c index 716203e..c69e996 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -25,10 +25,12 @@ #include #include #include +#include #include #include #include #include +#include #include "checkers.h" #include "prio.h" @@ -60,6 +62,9 @@ #include "uxsock.h" #include "mpath_cmd.h" #include "foreign.h" +#include "propsel.h" +#include "time-util.h" +#include "file.h" int logsink; struct udev *udev; @@ -70,7 +75,7 @@ struct config *get_multipath_config(void) return multipath_conf; } -void put_multipath_config(struct config *conf) +void put_multipath_config(void *arg) { /* Noop for now */ } @@ -350,6 +355,165 @@ out: return r; } +enum { + FIND_MULTIPATHS_WAIT_DONE = 0, + FIND_MULTIPATHS_WAITING = 1, + FIND_MULTIPATHS_ERROR = -1, + FIND_MULTIPATHS_NEVER = -2, +}; + +static const char shm_find_mp_dir[] = MULTIPATH_SHM_BASE "find_multipaths"; +static void close_fd(void *arg) +{ + close((long)arg); +} + +/** + * find_multipaths_check_timeout(wwid, tmo) + * Helper for "find_multipaths smart" + * + * @param[in] pp: path to check / record + * @param[in] tmo: configured timeout for this WWID, or value <= 0 for checking + * @param[out] until: timestamp until we must wait, CLOCK_REALTIME, if return + * value is FIND_MULTIPATHS_WAITING + * @returns: FIND_MULTIPATHS_WAIT_DONE, if waiting has finished + * @returns: FIND_MULTIPATHS_ERROR, if internal error occurred + * @returns: FIND_MULTIPATHS_NEVER, if tmo is 0 and we didn't wait for this + * device + * @returns: FIND_MULTIPATHS_WAITING, if timeout hasn't expired + */ +static int find_multipaths_check_timeout(const struct path *pp, long tmo, + struct timespec *until) +{ + char path[PATH_MAX]; + struct timespec now, ftimes[2], tdiff; + struct stat st; + long fd; + int r, err, retries = 0; + + clock_gettime(CLOCK_REALTIME, &now); + + if (snprintf(path, sizeof(path), "%s/%s", shm_find_mp_dir, pp->dev_t) + >= sizeof(path)) { + condlog(1, "%s: path name overflow", __func__); + return FIND_MULTIPATHS_ERROR; + } + + if (ensure_directories_exist(path, 0700)) { + condlog(1, "%s: error creating directories", __func__); + return FIND_MULTIPATHS_ERROR; + } + +retry: + fd = open(path, O_RDONLY); + if (fd != -1) { + pthread_cleanup_push(close_fd, (void *)fd); + r = fstat(fd, &st); + if (r != 0) + err = errno; + pthread_cleanup_pop(1); + + } else if (tmo > 0) { + if (errno == ENOENT) + fd = open(path, O_RDWR|O_EXCL|O_CREAT, 0644); + if (fd == -1) { + if (errno == EEXIST && !retries++) + /* We could have raced with another process */ + goto retry; + condlog(1, "%s: error opening %s: %s", + __func__, path, strerror(errno)); + return FIND_MULTIPATHS_ERROR; + }; + + pthread_cleanup_push(close_fd, (void *)fd); + /* + * We just created the file. Set st_mtim to our desired + * expiry time. + */ + ftimes[0].tv_sec = 0; + ftimes[0].tv_nsec = UTIME_OMIT; + ftimes[1].tv_sec = now.tv_sec + tmo; + ftimes[1].tv_nsec = now.tv_nsec; + if (futimens(fd, ftimes) != 0) { + condlog(1, "%s: error in futimens(%s): %s", __func__, + path, strerror(errno)); + } + r = fstat(fd, &st); + if (r != 0) + err = errno; + pthread_cleanup_pop(1); + } else + return FIND_MULTIPATHS_NEVER; + + if (r != 0) { + condlog(1, "%s: error in fstat for %s: %s", __func__, + path, strerror(err)); + return FIND_MULTIPATHS_ERROR; + } + + timespecsub(&st.st_mtim, &now, &tdiff); + + if (tdiff.tv_sec <= 0) + return FIND_MULTIPATHS_WAIT_DONE; + + *until = tdiff; + return FIND_MULTIPATHS_WAITING; +} + +static int print_cmd_valid(int k, const vector pathvec, + struct config *conf) +{ + static const int vals[] = { 1, 0, 2 }; + int wait = FIND_MULTIPATHS_NEVER; + struct timespec until; + struct path *pp; + + if (k < 0 || k >= sizeof(vals)) + return 1; + + if (k == 2) { + /* + * Caller ensures that pathvec[0] is the path to + * examine. + */ + pp = VECTOR_SLOT(pathvec, 0); + select_find_multipaths_timeout(conf, pp); + wait = find_multipaths_check_timeout( + pp, pp->find_multipaths_timeout, &until); + if (wait != FIND_MULTIPATHS_WAITING) + k = 1; + } else if (pathvec != NULL) { + pp = VECTOR_SLOT(pathvec, 0); + wait = find_multipaths_check_timeout(pp, 0, &until); + } + if (wait == FIND_MULTIPATHS_WAITING) + printf("FIND_MULTIPATHS_WAIT_UNTIL=\"%ld.%06ld\"\n", + until.tv_sec, until.tv_nsec/1000); + else if (wait == FIND_MULTIPATHS_WAIT_DONE) + printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n"); + printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n", vals[k]); + return k == 1; +} + +/* + * Returns true if this device has been handled before, + * and released to systemd. + * + * This must be called before get_refwwid(), + * otherwise udev_device_new_from_environment() will have + * destroyed environ(7). + */ +static bool released_to_systemd(void) +{ + static const char dmdp[] = "DM_MULTIPATH_DEVICE_PATH"; + const char *dm_mp_dev_path = getenv(dmdp); + bool ret; + + ret = dm_mp_dev_path != NULL && !strcmp(dm_mp_dev_path, "0"); + condlog(4, "%s: %s=%s -> %d", __func__, dmdp, dm_mp_dev_path, ret); + return ret; +} + /* * Return value: * -1: Retry @@ -367,6 +531,7 @@ configure (struct config *conf, enum mpath_cmds cmd, int di_flag = 0; char * refwwid = NULL; char * dev = NULL; + bool released = released_to_systemd(); /* * allocate core vectors to store paths and multipaths @@ -391,10 +556,7 @@ configure (struct config *conf, enum mpath_cmds cmd, cmd != CMD_REMOVE_WWID && (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) { - if (cmd == CMD_VALID_PATH) - printf("%s is not a valid multipath device path\n", - devpath); - goto out; + goto print_valid; } /* @@ -407,7 +569,7 @@ configure (struct config *conf, enum mpath_cmds cmd, if (!refwwid) { condlog(4, "%s: failed to get wwid", devpath); if (failed == 2 && cmd == CMD_VALID_PATH) - printf("%s is not a valid multipath device path\n", devpath); + goto print_valid; else condlog(3, "scope is null"); goto out; @@ -425,7 +587,7 @@ configure (struct config *conf, enum mpath_cmds cmd, } if (cmd == CMD_ADD_WWID) { r = remember_wwid(refwwid); - if (r == 0) + if (r >= 0) printf("wwid '%s' added\n", refwwid); else printf("failed adding '%s' to wwids file\n", @@ -437,16 +599,47 @@ configure (struct config *conf, enum mpath_cmds cmd, * set, you need to actually check if there are two available * paths to determine if this path should be multipathed. To * do this, we put off the check until after discovering all - * the paths */ - if (cmd == CMD_VALID_PATH && - (!conf->find_multipaths || !conf->ignore_wwids)) { - if (conf->ignore_wwids || - check_wwids_file(refwwid, 0) == 0) + * the paths. + * Paths listed in the wwids file are always considered valid. + */ + if (cmd == CMD_VALID_PATH) { + if (is_failed_wwid(refwwid) == WWID_IS_FAILED) { + r = 1; + goto print_valid; + } + if ((!find_multipaths_on(conf) && + ignore_wwids_on(conf)) || + check_wwids_file(refwwid, 0) == 0) r = 0; + if (!ignore_wwids_on(conf)) + goto print_valid; + /* At this point, either r==0 or find_multipaths_on. */ + + /* + * Shortcut for find_multipaths smart: + * Quick check if path is already multipathed. + */ + if (sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0))) { + r = 0; + goto print_valid; + } - printf("%s %s a valid multipath device path\n", - devpath, r == 0 ? "is" : "is not"); - goto out; + /* + * DM_MULTIPATH_DEVICE_PATH=="0" means that we have + * been called for this device already, and have + * released it to systemd. Unless the device is now + * already multipathed (see above), we can't try to + * grab it, because setting SYSTEMD_READY=0 would + * cause file systems to be unmounted. + * Leave DM_MULTIPATH_DEVICE_PATH="0". + */ + if (released) { + r = 1; + goto print_valid; + } + if (r == 0) + goto print_valid; + /* find_multipaths_on: Fall through to path detection */ } } @@ -482,16 +675,56 @@ configure (struct config *conf, enum mpath_cmds cmd, if (cmd == CMD_VALID_PATH) { + struct path *pp; + int fd; + /* This only happens if find_multipaths and - * ignore_wwids is set. + * ignore_wwids is set, and the path is not in WWIDs + * file, not currently multipathed, and has + * never been released to systemd. * If there is currently a multipath device matching * the refwwid, or there is more than one path matching * the refwwid, then the path is valid */ - if (VECTOR_SIZE(curmp) != 0 || VECTOR_SIZE(pathvec) > 1) + if (VECTOR_SIZE(curmp) != 0) { r = 0; - printf("%s %s a valid multipath device path\n", - devpath, r == 0 ? "is" : "is not"); - goto out; + goto print_valid; + } else if (VECTOR_SIZE(pathvec) > 1) + r = 0; + else + /* Use r=2 as an indication for "maybe" */ + r = 2; + + /* + * If opening the path with O_EXCL fails, the path + * is in use (e.g. mounted during initramfs processing). + * We know that it's not used by dm-multipath. + * We may not set SYSTEMD_READY=0 on such devices, it + * might cause systemd to umount the device. + * Use O_RDONLY, because udevd would trigger another + * uevent for close-after-write. + * + * The O_EXCL check is potentially dangerous, because it may + * race with other tasks trying to access the device. Therefore + * this code is only executed if the path hasn't been released + * to systemd earlier (see above). + * + * get_refwwid() above stores the path we examine in slot 0. + */ + pp = VECTOR_SLOT(pathvec, 0); + fd = open(udev_device_get_devnode(pp->udev), + O_RDONLY|O_EXCL); + if (fd >= 0) + close(fd); + else { + condlog(3, "%s: path %s is in use: %s", + __func__, pp->dev, + strerror(errno)); + /* + * Check if we raced with multipathd + */ + r = !sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0)); + } + goto print_valid; } if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) { @@ -505,6 +738,10 @@ configure (struct config *conf, enum mpath_cmds cmd, r = coalesce_paths(&vecs, NULL, refwwid, conf->force_reload, cmd); +print_valid: + if (cmd == CMD_VALID_PATH) + r = print_cmd_valid(r, pathvec, conf); + out: if (refwwid) FREE(refwwid); @@ -734,7 +971,7 @@ main (int argc, char *argv[]) conf->force_reload = FORCE_RELOAD_YES; break; case 'i': - conf->ignore_wwids = 1; + conf->find_multipaths |= _FIND_MULTIPATHS_I; break; case 't': r = dump_config(conf); @@ -776,16 +1013,6 @@ main (int argc, char *argv[]) } } - /* - * FIXME: new device detection with find_multipaths currently - * doesn't work reliably. - */ - if (cmd == CMD_VALID_PATH && - conf->find_multipaths && conf->ignore_wwids) { - condlog(2, "ignoring -i flag because find_multipath is set in multipath.conf"); - conf->ignore_wwids = 0; - } - if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); @@ -850,8 +1077,7 @@ main (int argc, char *argv[]) if (fd == -1) { condlog(3, "%s: daemon is not running", dev); if (!systemd_service_enabled(dev)) { - printf("%s is not a valid " - "multipath device path\n", dev); + r = print_cmd_valid(1, NULL, conf); goto out; } } else @@ -912,6 +1138,13 @@ out: cleanup_prio(); cleanup_checkers(); + /* + * multipath -u must exit with status 0, otherwise udev won't + * import its output. + */ + if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT && r == 1) + r = 0; + if (dev_type == DEV_UEVENT) closelog(); diff --git a/multipath/multipath.8 b/multipath/multipath.8 index 56f8703..914a8cb 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -94,7 +94,14 @@ Force devmap reload. . .TP .B \-i -Ignore WWIDs file when processing devices. +Ignore WWIDs file when processing devices. If +\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in +\fImultipath.conf\fR, multipath only considers devices that are +listed in the WWIDs file. This option overrides that behavior. For other values +of \fIfind_multipaths\fR, this option has no effect. See the description of +\fIfind_multipaths\fR in +.BR multipath.conf (5). +This option should only be used in rare circumstances. . .TP .B \-B diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index c4d0789..f689795 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -951,28 +951,65 @@ The default is: \fBno\fR . .TP .B find_multipaths -If set to -.I yes -, instead of trying to create a multipath device for every non-blacklisted -path, multipath will only create a device if one of three condidions are -met. -.I 1 -There are at least two non-blacklisted paths with the same WWID, -.I 2 -the user manually forces the creation, by specifying a device with the multipath -command, or -.I 3 -a path has the same WWID as a multipath device that was previously created -while find_multipaths was set (even if that multipath device doesn't currently -exist). -Whenever a multipath device is created with find_multipaths set, multipath will -remember the WWID of the device, so that it will automatically create the -device again, as soon as it sees a path with that WWID. This should allow most -users to have multipath automatically choose the correct paths to make into -multipath devices, without having to edit the blacklist. -.RS +This option controls whether multipath and multipathd try to create multipath +maps over non-blacklisted devices they encounter. This matters a) when a device is +encountered by \fBmultipath -u\fR during udev rule processing (a device is +blocked from further processing by higher layers - such as LVM - if and only +if it\'s considered a valid multipath device path), and b) when multipathd +detects a new device. The following values are possible: +.RS +.TP 10 +.I strict +Both multipath and multipathd treat only such devices as multipath devices +which have been part of a multipath map previously, and which are therefore +listed in the \fBwwids_file\fR. Users can manually set up multipath maps using the +\fBmultipathd add map\fR command. Once set up manually, the map is +remembered in the wwids file and will be set up automatically in the future. .TP -The default is: \fBno\fR +.I no +Multipath behaves like \fBstrict\fR. Multipathd behaves like \fBgreedy\fR. +.TP +.I yes +Both multipathd and multipath treat a device as multipath device if the +conditions for \fBstrict\fR are met, or if at least two non-blacklisted paths +with the same WWID have been detected. +.TP +.I greedy +Both multipathd and multipath treat every non-blacklisted device as multipath +device path. +.TP +.I smart +This differs from \fIfind_multipaths yes\fR only in +the way it treats new devices for which only one path has been +detected yet. When such a device is first encounted in udev rules, it is +treated as a multipath device. multipathd waits whether additional paths with +the same WWID appears. If that happens, it sets up a multipath map. If it +doesn\'t happen until a +timeout expires, or if setting up the map fails, a new uevent is triggered for +the device; at second encounter in the udev rules, the device will be treated +as non-multipath and passed on to upper layers. +\fBNote:\fR this may cause delays during device detection if +there are single-path devices which aren\'t blacklisted. +.TP +The default is: \fBstrict\fR +.RE +. +. +.TP +.B find_multipaths_timeout +Timeout, in seconds, to wait for additional paths after detecting the first +one, if \fIfind_multipaths +"smart"\fR (see above) is set. If the value is \fBpositive\fR, this timeout is used for all +unknown, non-blacklisted devices encountered. If the value is \fBnegative\fR +(recommended), it's only +applied to "known" devices that have an entry in multipath's hardware table, +either in the built-in table or in a \fIdevice\fR section; other ("unknown") devices will +use a timeout of only 1 second to avoid booting delays. The value 0 means +"use the built-in default". If \fIfind_multipath\fR has a value +other than \fIsmart\fR, this option has no effect. +.RS +.TP +The default is: \fB-10\fR (10s for known and 1s for unknown hardware) .RE . . diff --git a/multipath/multipath.rules b/multipath/multipath.rules index 6f8ee2b..d658073 100644 --- a/multipath/multipath.rules +++ b/multipath/multipath.rules @@ -1,7 +1,9 @@ # Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath SUBSYSTEM!="block", GOTO="end_mpath" -ACTION!="add|change", GOTO="end_mpath" KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath" +ACTION=="remove", TEST=="/dev/shm/multipath/find_multipaths/$major:$minor", \ + RUN+="/usr/bin/rm -f /dev/shm/multipath/find_multipaths/$major:$minor" +ACTION!="add|change", GOTO="end_mpath" IMPORT{cmdline}="nompath" ENV{nompath}=="?*", GOTO="end_mpath" @@ -19,9 +21,67 @@ LABEL="test_dev" ENV{MPATH_SBIN_PATH}="/sbin" TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" -ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \ - PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \ - ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="mpath_member", \ - ENV{SYSTEMD_READY}="0" +# FIND_MULTIPATHS_WAIT_UNTIL is the timeout (in seconds after the +# epoch). +IMPORT{db}="FIND_MULTIPATHS_WAIT_UNTIL" +ENV{.SAVED_FM_WAIT_UNTIL}="$env{FIND_MULTIPATHS_WAIT_UNTIL}" + +# multipath -u needs to know if this device has ever been exported +IMPORT{db}="DM_MULTIPATH_DEVICE_PATH" + +# multipath -u sets DM_MULTIPATH_DEVICE_PATH and, +# if "find_multipaths smart", also FIND_MULTIPATHS_WAIT_UNTIL. +IMPORT{program}="$env{MPATH_SBIN_PATH}/multipath -u %k" + +# case 1: this is definitely multipath +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", \ + ENV{ID_FS_TYPE}="mpath_member", ENV{SYSTEMD_READY}="0", \ + GOTO="stop_wait" + +# case 2: this is definitely not multipath, or timeout has expired +ENV{DM_MULTIPATH_DEVICE_PATH}!="2", \ + GOTO="stop_wait" + +# Code below here is only run in "smart" mode. +# multipath -u has indicated this is "maybe" multipath. + +# This shouldn't happen, just in case. +ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="?*", GOTO="end_mpath" + +# Be careful not to start the timer twice. +ACTION!="add", GOTO="pretend_mpath" +ENV{.SAVED_FM_WAIT_UNTIL}=="?*", GOTO="pretend_mpath" + +# At this point, we are seeing this path for the first time, and it's "maybe" multipath. + +# The actual start command for the timer. +# +# The purpose of this command is only to make sure we will receive another +# uevent eventually. *Any* uevent may cause waiting to finish if it either ends +# in case 1-3 above, or if it arrives after FIND_MULTIPATHS_WAIT_UNTIL. +# +# Note that this will try to activate multipathd if it isn't running yet. +# If that fails, the unit starts and expires nonetheless. If multipathd +# startup needs to wait for other services, this wait time will add up with +# the --on-active timeout. +# +# We must trigger an "add" event because LVM2 will only act on those. + +RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Wants=multipathd.service --property After=multipathd.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /usr/bin/udevadm trigger --action=add $sys$devpath" + +LABEL="pretend_mpath" +ENV{DM_MULTIPATH_DEVICE_PATH}="1" +ENV{SYSTEMD_READY}="0" +GOTO="end_mpath" + +LABEL="stop_wait" +# If timeout hasn't expired but we're not in "maybe" state any more, stop timer +# Do this only once, and only if the timer has been started before +IMPORT{db}="FIND_MULTIPATHS_WAIT_CANCELLED" +ENV{FIND_MULTIPATHS_WAIT_CANCELLED}!="?*", \ + ENV{FIND_MULTIPATHS_WAIT_UNTIL}=="?*", \ + ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="0", \ + ENV{FIND_MULTIPATHS_WAIT_CANCELLED}="1", \ + RUN+="/usr/bin/systemctl stop cancel-multipath-wait-$kernel.timer" LABEL="end_mpath" diff --git a/multipathd/Makefile b/multipathd/Makefile index 4c9d296..d1a9863 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -21,8 +21,12 @@ ifdef SYSTEMD LIBDEPS += -lsystemd-daemon endif endif +ifeq ($(ENABLE_DMEVENTS_POLL),0) + CFLAGS += -DNO_DMEVENTS_POLL +endif -OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o +OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \ + dmevents.o EXEC = multipathd diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 60ec48b..ba50fb8 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -28,6 +28,7 @@ #include "cli.h" #include "uevent.h" #include "foreign.h" +#include "cli_handlers.h" int show_paths (char ** r, int * len, struct vectors * vecs, char * style, @@ -252,14 +253,16 @@ show_config (char ** r, int * len) unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; struct config *conf; + int fail = 0; c = reply = MALLOC(maxlen); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); while (again) { if (!reply) { - put_multipath_config(conf); - return 1; + fail = 1; + break; } c = reply; c += snprint_defaults(conf, c, reply + maxlen - c); @@ -296,7 +299,9 @@ show_config (char ** r, int * len) REALLOC_REPLY(reply, again, maxlen); } } - put_multipath_config(conf); + pthread_cleanup_pop(1); + if (fail) + return 1; *r = reply; *len = (int)(c - reply + 1); return 0; @@ -714,34 +719,37 @@ cli_add_path (void * v, char ** reply, int * len, void * data) struct path *pp; int r; struct config *conf; + int invalid = 0; param = convert_dev(param, 1); condlog(2, "%s: add path (operator)", param); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, - param) > 0) { - put_multipath_config(conf); + param) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) goto blacklisted; - } pp = find_path_by_dev(vecs->pathvec, param); if (pp) { condlog(2, "%s: path already in pathvec", param); - if (pp->mpp) { - put_multipath_config(conf); + if (pp->mpp) return 0; - } } else { struct udev_device *udevice; udevice = udev_device_new_from_subsystem_sysname(udev, "block", param); + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = store_pathinfo(vecs->pathvec, conf, udevice, DI_ALL, &pp); + pthread_cleanup_pop(1); udev_device_unref(udevice); if (!pp) { - put_multipath_config(conf); if (r == 2) goto blacklisted; condlog(0, "%s: failed to store path info", param); @@ -749,7 +757,6 @@ cli_add_path (void * v, char ** reply, int * len, void * data) } pp->checkint = conf->checkint; } - put_multipath_config(conf); return ev_add_path(pp, vecs, 1); blacklisted: *reply = strdup("blacklisted\n"); @@ -785,19 +792,22 @@ cli_add_map (void * v, char ** reply, int * len, void * data) char *refwwid, *alias = NULL; int rc, count = 0; struct config *conf; + int invalid = 0; param = convert_dev(param, 0); condlog(2, "%s: add map (operator)", param); conf = get_multipath_config(); - if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) { - put_multipath_config(conf); + pthread_cleanup_push(put_multipath_config, conf); + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) + invalid = 1; + pthread_cleanup_pop(1); + if (invalid) { *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 1; } - put_multipath_config(conf); do { if (dm_get_major_minor(param, &major, &minor) < 0) condlog(2, "%s: not a device mapper table", param); @@ -975,9 +985,10 @@ cli_resize(void *v, char **reply, int *len, void *data) int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) { - struct config *conf = get_multipath_config(); + struct config *conf; condlog(2, "force queue_without_daemon (operator)"); + conf = get_multipath_config(); if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) conf->queue_without_daemon = QUE_NO_DAEMON_FORCE; put_multipath_config(conf); @@ -987,9 +998,10 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data) { - struct config *conf = get_multipath_config(); + struct config *conf; condlog(2, "restore queue_without_daemon (operator)"); + conf = get_multipath_config(); if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE) conf->queue_without_daemon = QUE_NO_DAEMON_OFF; put_multipath_config(conf); @@ -1017,10 +1029,11 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data) return 1; } - conf = get_multipath_config(); mpp->disable_queueing = 0; + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); select_no_path_retry(conf, mpp); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { @@ -1044,10 +1057,11 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data) condlog(2, "restore queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { - struct config *conf = get_multipath_config(); mpp->disable_queueing = 0; + struct config *conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); select_no_path_retry(conf, mpp); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { dm_queue_if_no_path(mpp->alias, 1); @@ -1279,14 +1293,17 @@ show_blacklist (char ** r, int * len) char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; - struct config *conf = get_multipath_config(); + struct config *conf; + int fail = 0; reply = MALLOC(maxlen); + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); while (again) { if (!reply) { - put_multipath_config(conf); - return 1; + fail = 1; + break; } c = reply; @@ -1294,11 +1311,12 @@ show_blacklist (char ** r, int * len) again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } + pthread_cleanup_pop(1); + if (fail) + return 1; *r = reply; *len = (int)(c - reply + 1); - put_multipath_config(conf); - return 0; } @@ -1317,14 +1335,17 @@ show_devices (char ** r, int * len, struct vectors *vecs) char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; - struct config *conf = get_multipath_config(); + struct config *conf; + int fail = 0; reply = MALLOC(maxlen); + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); while (again) { if (!reply) { - put_multipath_config(conf); - return 1; + fail = 1; + break; } c = reply; @@ -1332,10 +1353,12 @@ show_devices (char ** r, int * len, struct vectors *vecs) again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } + pthread_cleanup_pop(1); + if (fail) + return 1; *r = reply; *len = (int)(c - reply + 1); - put_multipath_config(conf); return 0; } @@ -1479,8 +1502,9 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data) return 1; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = set_prkey(conf, mpp, 0); - put_multipath_config(conf); + pthread_cleanup_pop(1); return ret; } @@ -1509,8 +1533,9 @@ cli_setprkey(void * v, char ** reply, int * len, void * data) } conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = set_prkey(conf, mpp, prkey); - put_multipath_config(conf); + pthread_cleanup_pop(1); return ret; } diff --git a/multipathd/dmevents.c b/multipathd/dmevents.c new file mode 100644 index 0000000..e98a974 --- /dev/null +++ b/multipathd/dmevents.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + * Copyright (c) 2005 Kiyoshi Ueda, NEC + * Copyright (c) 2005 Edward Goggin, EMC + * Copyright (c) 2005, 2018 Benjamin Marzinski, Redhat + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vector.h" +#include "structs.h" +#include "structs_vec.h" +#include "devmapper.h" +#include "debug.h" +#include "main.h" +#include "dmevents.h" +#include "util.h" + +#ifndef DM_DEV_ARM_POLL +#define DM_DEV_ARM_POLL _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD + 1, struct dm_ioctl) +#endif + +enum event_actions { + EVENT_NOTHING, + EVENT_REMOVE, + EVENT_UPDATE, +}; + +struct dev_event { + char name[WWID_SIZE]; + uint32_t evt_nr; + enum event_actions action; +}; + +struct dmevent_waiter { + int fd; + struct vectors *vecs; + vector events; + pthread_mutex_t events_lock; +}; + +static struct dmevent_waiter *waiter; + +int dmevent_poll_supported(void) +{ + unsigned int minv[3] = {4, 37, 0}; + unsigned int v[3]; + + if (dm_drv_version(v)) + return 0; + + if (VERSION_GE(v, minv)) + return 1; + return 0; +} + + +int init_dmevent_waiter(struct vectors *vecs) +{ + if (!vecs) { + condlog(0, "can't create waiter structure. invalid vectors"); + goto fail; + } + waiter = (struct dmevent_waiter *)malloc(sizeof(struct dmevent_waiter)); + if (!waiter) { + condlog(0, "failed to allocate waiter structure"); + goto fail; + } + memset(waiter, 0, sizeof(struct dmevent_waiter)); + waiter->events = vector_alloc(); + if (!waiter->events) { + condlog(0, "failed to allocate waiter events vector"); + goto fail_waiter; + } + waiter->fd = open("/dev/mapper/control", O_RDWR); + if (waiter->fd < 0) { + condlog(0, "failed to open /dev/mapper/control for waiter"); + goto fail_events; + } + pthread_mutex_init(&waiter->events_lock, NULL); + waiter->vecs = vecs; + + return 0; +fail_events: + vector_free(waiter->events); +fail_waiter: + free(waiter); +fail: + waiter = NULL; + return -1; +} + +void cleanup_dmevent_waiter(void) +{ + struct dev_event *dev_evt; + int i; + + if (!waiter) + return; + pthread_mutex_destroy(&waiter->events_lock); + close(waiter->fd); + vector_foreach_slot(waiter->events, dev_evt, i) + free(dev_evt); + vector_free(waiter->events); + free(waiter); + waiter = NULL; +} + +static int arm_dm_event_poll(int fd) +{ + struct dm_ioctl dmi; + memset(&dmi, 0, sizeof(dmi)); + dmi.version[0] = DM_VERSION_MAJOR; + dmi.version[1] = DM_VERSION_MINOR; + dmi.version[2] = DM_VERSION_PATCHLEVEL; + /* This flag currently does nothing. It simply exists to + * duplicate the behavior of libdevmapper */ + dmi.flags = 0x4; + dmi.data_start = offsetof(struct dm_ioctl, data); + dmi.data_size = sizeof(dmi); + return ioctl(fd, DM_DEV_ARM_POLL, &dmi); +} + +/* + * As of version 4.37.0 device-mapper stores the event number in the + * dm_names structure after the name, when DM_DEVICE_LIST is called + */ +static uint32_t dm_event_nr(struct dm_names *n) +{ + return *(uint32_t *)(((uintptr_t)(strchr(n->name, 0) + 1) + 7) & ~7); +} + +static int dm_get_events(void) +{ + struct dm_task *dmt; + struct dm_names *names; + struct dev_event *dev_evt; + int i; + + if (!(dmt = libmp_dm_task_create(DM_DEVICE_LIST))) + return -1; + + dm_task_no_open_count(dmt); + + if (!dm_task_run(dmt)) + goto fail; + + if (!(names = dm_task_get_names(dmt))) + goto fail; + + pthread_mutex_lock(&waiter->events_lock); + vector_foreach_slot(waiter->events, dev_evt, i) + dev_evt->action = EVENT_REMOVE; + while (names->dev) { + uint32_t event_nr; + + if (!dm_is_mpath(names->name)) + goto next; + + event_nr = dm_event_nr(names); + vector_foreach_slot(waiter->events, dev_evt, i) { + if (!strcmp(dev_evt->name, names->name)) { + if (event_nr != dev_evt->evt_nr) { + dev_evt->evt_nr = event_nr; + dev_evt->action = EVENT_UPDATE; + } else + dev_evt->action = EVENT_NOTHING; + break; + } + } +next: + if (!names->next) + break; + names = (void *)names + names->next; + } + pthread_mutex_unlock(&waiter->events_lock); + dm_task_destroy(dmt); + return 0; + +fail: + dm_task_destroy(dmt); + return -1; +} + +/* You must call __setup_multipath() after calling this function, to + * deal with any events that came in before the device was added */ +int watch_dmevents(char *name) +{ + int event_nr; + struct dev_event *dev_evt, *old_dev_evt; + int i; + + if (!dm_is_mpath(name)) { + condlog(0, "%s: not a multipath device. can't watch events", + name); + return -1; + } + + if ((event_nr = dm_geteventnr(name)) < 0) + return -1; + + dev_evt = (struct dev_event *)malloc(sizeof(struct dev_event)); + if (!dev_evt) { + condlog(0, "%s: can't allocate event waiter structure", name); + return -1; + } + + strlcpy(dev_evt->name, name, WWID_SIZE); + dev_evt->evt_nr = event_nr; + dev_evt->action = EVENT_NOTHING; + + pthread_mutex_lock(&waiter->events_lock); + vector_foreach_slot(waiter->events, old_dev_evt, i){ + if (!strcmp(dev_evt->name, old_dev_evt->name)) { + /* caller will be updating this device */ + old_dev_evt->evt_nr = event_nr; + old_dev_evt->action = EVENT_NOTHING; + pthread_mutex_unlock(&waiter->events_lock); + condlog(2, "%s: already waiting for events on device", + name); + free(dev_evt); + return 0; + } + } + if (!vector_alloc_slot(waiter->events)) { + pthread_mutex_unlock(&waiter->events_lock); + free(dev_evt); + return -1; + } + vector_set_slot(waiter->events, dev_evt); + pthread_mutex_unlock(&waiter->events_lock); + return 0; +} + +void unwatch_all_dmevents(void) +{ + struct dev_event *dev_evt; + int i; + + pthread_mutex_lock(&waiter->events_lock); + vector_foreach_slot(waiter->events, dev_evt, i) + free(dev_evt); + vector_reset(waiter->events); + pthread_mutex_unlock(&waiter->events_lock); +} + +static void unwatch_dmevents(char *name) +{ + struct dev_event *dev_evt; + int i; + + pthread_mutex_lock(&waiter->events_lock); + vector_foreach_slot(waiter->events, dev_evt, i) { + if (!strcmp(dev_evt->name, name)) { + vector_del_slot(waiter->events, i); + free(dev_evt); + break; + } + } + pthread_mutex_unlock(&waiter->events_lock); +} + +/* + * returns the reschedule delay + * negative means *stop* + */ + +/* poll, arm, update, return */ +static int dmevent_loop (void) +{ + int r, i = 0; + struct pollfd pfd; + struct dev_event *dev_evt; + + pfd.fd = waiter->fd; + pfd.events = POLLIN; + r = poll(&pfd, 1, -1); + if (r <= 0) { + condlog(0, "failed polling for dm events: %s", strerror(errno)); + /* sleep 1s and hope things get better */ + return 1; + } + + if (arm_dm_event_poll(waiter->fd) != 0) { + condlog(0, "Cannot re-arm event polling: %s", strerror(errno)); + /* sleep 1s and hope things get better */ + return 1; + } + + if (dm_get_events() != 0) { + condlog(0, "failed getting dm events: %s", strerror(errno)); + /* sleep 1s and hope things get better */ + return 1; + } + + /* + * upon event ... + */ + + while (1) { + int done = 1; + struct dev_event curr_dev; + + pthread_mutex_lock(&waiter->events_lock); + vector_foreach_slot(waiter->events, dev_evt, i) { + if (dev_evt->action != EVENT_NOTHING) { + curr_dev = *dev_evt; + if (dev_evt->action == EVENT_REMOVE) { + vector_del_slot(waiter->events, i); + free(dev_evt); + } else + dev_evt->action = EVENT_NOTHING; + done = 0; + break; + } + } + pthread_mutex_unlock(&waiter->events_lock); + if (done) + return 1; + + condlog(3, "%s: devmap event #%i", curr_dev.name, + curr_dev.evt_nr); + + /* + * event might be : + * + * 1) a table reload, which means our mpp structure is + * obsolete : refresh it through update_multipath() + * 2) a path failed by DM : mark as such through + * update_multipath() + * 3) map has gone away : stop the thread. + * 4) a path reinstate : nothing to do + * 5) a switch group : nothing to do + */ + pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock); + lock(&waiter->vecs->lock); + pthread_testcancel(); + r = 0; + if (curr_dev.action == EVENT_REMOVE) + remove_map_by_alias(curr_dev.name, waiter->vecs, 1); + else + r = update_multipath(waiter->vecs, curr_dev.name, 1); + pthread_cleanup_pop(1); + + if (r) { + condlog(2, "%s: stopped watching dmevents", + curr_dev.name); + unwatch_dmevents(curr_dev.name); + } + } + condlog(0, "dmevent waiter thread unexpectedly quit"); + return -1; /* never reach there */ +} + +static void rcu_unregister(void *param) +{ + rcu_unregister_thread(); +} + +void *wait_dmevents (void *unused) +{ + int r; + + + if (!waiter) { + condlog(0, "dmevents waiter not intialized"); + return NULL; + } + + pthread_cleanup_push(rcu_unregister, NULL); + rcu_register_thread(); + mlockall(MCL_CURRENT | MCL_FUTURE); + + while (1) { + r = dmevent_loop(); + + if (r < 0) + break; + + sleep(r); + } + + pthread_cleanup_pop(1); + return NULL; +} diff --git a/multipathd/dmevents.h b/multipathd/dmevents.h new file mode 100644 index 0000000..012fbad --- /dev/null +++ b/multipathd/dmevents.h @@ -0,0 +1,13 @@ +#ifndef _DMEVENTS_H +#define _DMEVENTS_H + +#include "structs_vec.h" + +int dmevent_poll_supported(void); +int init_dmevent_waiter(struct vectors *vecs); +void cleanup_dmevent_waiter(void); +int watch_dmevents(char *name); +void unwatch_all_dmevents(void); +void *wait_dmevents (void *unused); + +#endif /* _DMEVENTS_H */ diff --git a/multipathd/main.c b/multipathd/main.c index a03ba20..0db88ee 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -82,6 +82,7 @@ static int use_watchdog; #include "cli_handlers.h" #include "lock.h" #include "waiter.h" +#include "dmevents.h" #include "io_err_stat.h" #include "wwids.h" #include "foreign.h" @@ -109,6 +110,11 @@ int uxsock_timeout; int verbosity; int bindings_read_only; int ignore_new_devs; +#ifdef NO_DMEVENTS_POLL +int poll_dmevents = 0; +#else +int poll_dmevents = 1; +#endif enum daemon_status running_state = DAEMON_INIT; pid_t daemon_pid; pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER; @@ -240,7 +246,7 @@ struct config *get_multipath_config(void) return rcu_dereference(multipath_conf); } -void put_multipath_config(struct config *conf) +void put_multipath_config(void *arg) { rcu_read_unlock(); } @@ -264,8 +270,10 @@ need_switch_pathgroup (struct multipath * mpp, int refresh) vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, + conf); pathinfo(pp, conf, DI_PRIO); - put_multipath_config(conf); + pthread_cleanup_pop(1); } } } @@ -293,6 +301,250 @@ switch_pathgroup (struct multipath * mpp) mpp->alias, mpp->bestpg); } +static int +wait_for_events(struct multipath *mpp, struct vectors *vecs) +{ + if (poll_dmevents) + return watch_dmevents(mpp->alias); + else + return start_waiter_thread(mpp, vecs); +} + +static void +remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs) +{ + /* devices are automatically removed by the dmevent polling code, + * so they don't need to be manually removed here */ + if (!poll_dmevents) + stop_waiter_thread(mpp, vecs); + remove_map(mpp, vecs, PURGE_VEC); +} + +static void +remove_maps_and_stop_waiters(struct vectors *vecs) +{ + int i; + struct multipath * mpp; + + if (!vecs) + return; + + if (!poll_dmevents) { + vector_foreach_slot(vecs->mpvec, mpp, i) + stop_waiter_thread(mpp, vecs); + } + else + unwatch_all_dmevents(); + + remove_maps(vecs); +} + +static void +set_multipath_wwid (struct multipath * mpp) +{ + if (strlen(mpp->wwid)) + return; + + dm_get_uuid(mpp->alias, mpp->wwid); +} + +static void set_no_path_retry(struct multipath *mpp) +{ + char is_queueing = 0; + + mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); + if (mpp->features && strstr(mpp->features, "queue_if_no_path")) + is_queueing = 1; + + switch (mpp->no_path_retry) { + case NO_PATH_RETRY_UNDEF: + break; + case NO_PATH_RETRY_FAIL: + if (is_queueing) + dm_queue_if_no_path(mpp->alias, 0); + break; + case NO_PATH_RETRY_QUEUE: + if (!is_queueing) + dm_queue_if_no_path(mpp->alias, 1); + break; + default: + if (mpp->nr_active > 0) { + mpp->retry_tick = 0; + dm_queue_if_no_path(mpp->alias, 1); + } else if (is_queueing && mpp->retry_tick == 0) + enter_recovery_mode(mpp); + break; + } +} + +int __setup_multipath(struct vectors *vecs, struct multipath *mpp, + int reset) +{ + if (dm_get_info(mpp->alias, &mpp->dmi)) { + /* Error accessing table */ + condlog(3, "%s: cannot access table", mpp->alias); + goto out; + } + + if (update_multipath_strings(mpp, vecs->pathvec, 1)) { + condlog(0, "%s: failed to setup multipath", mpp->alias); + goto out; + } + + if (reset) { + set_no_path_retry(mpp); + if (VECTOR_SIZE(mpp->paths) != 0) + dm_cancel_deferred_remove(mpp); + } + + return 0; +out: + remove_map_and_stop_waiter(mpp, vecs); + return 1; +} + +int update_multipath (struct vectors *vecs, char *mapname, int reset) +{ + struct multipath *mpp; + struct pathgroup *pgp; + struct path *pp; + int i, j; + + mpp = find_mp_by_alias(vecs->mpvec, mapname); + + if (!mpp) { + condlog(3, "%s: multipath map not found", mapname); + return 2; + } + + if (__setup_multipath(vecs, mpp, reset)) + return 1; /* mpp freed in setup_multipath */ + + /* + * compare checkers states with DM states + */ + vector_foreach_slot (mpp->pg, pgp, i) { + vector_foreach_slot (pgp->paths, pp, j) { + if (pp->dmstate != PSTATE_FAILED) + continue; + + if (pp->state != PATH_DOWN) { + struct config *conf = get_multipath_config(); + int oldstate = pp->state; + int checkint; + + conf = get_multipath_config(); + checkint = conf->checkint; + put_multipath_config(conf); + condlog(2, "%s: mark as failed", pp->dev); + mpp->stat_path_failures++; + pp->state = PATH_DOWN; + if (oldstate == PATH_UP || + oldstate == PATH_GHOST) + update_queue_mode_del_path(mpp); + + /* + * if opportune, + * schedule the next check earlier + */ + if (pp->tick > checkint) + pp->tick = checkint; + } + } + } + return 0; +} + +static int +update_map (struct multipath *mpp, struct vectors *vecs, int new_map) +{ + int retries = 3; + char params[PARAMS_SIZE] = {0}; + +retry: + condlog(4, "%s: updating new map", mpp->alias); + if (adopt_paths(vecs->pathvec, mpp)) { + condlog(0, "%s: failed to adopt paths for new map update", + mpp->alias); + retries = -1; + goto fail; + } + verify_paths(mpp, vecs); + mpp->action = ACT_RELOAD; + + extract_hwe_from_path(mpp); + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { + condlog(0, "%s: failed to setup new map in update", mpp->alias); + retries = -1; + goto fail; + } + if (domap(mpp, params, 1) <= 0 && retries-- > 0) { + condlog(0, "%s: map_udate sleep", mpp->alias); + sleep(1); + goto retry; + } + dm_lib_release(); + +fail: + if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) { + condlog(0, "%s: failed to create new map", mpp->alias); + remove_map(mpp, vecs, 1); + return 1; + } + + if (setup_multipath(vecs, mpp)) + return 1; + + sync_map_state(mpp); + + if (retries < 0) + condlog(0, "%s: failed reload in new map update", mpp->alias); + return 0; +} + +static struct multipath * +add_map_without_path (struct vectors *vecs, const char *alias) +{ + struct multipath * mpp = alloc_multipath(); + struct config *conf; + + if (!mpp) + return NULL; + if (!alias) { + FREE(mpp); + return NULL; + } + + mpp->alias = STRDUP(alias); + + if (dm_get_info(mpp->alias, &mpp->dmi)) { + condlog(3, "%s: cannot access table", mpp->alias); + goto out; + } + set_multipath_wwid(mpp); + conf = get_multipath_config(); + mpp->mpe = find_mpe(conf->mptable, mpp->wwid); + put_multipath_config(conf); + + if (update_multipath_table(mpp, vecs->pathvec, 1)) + goto out; + if (update_multipath_status(mpp)) + goto out; + + if (!vector_alloc_slot(vecs->mpvec)) + goto out; + + vector_set_slot(vecs->mpvec, mpp); + + if (update_map(mpp, vecs, 1) != 0) /* map removed */ + return NULL; + + return mpp; +out: + remove_map(mpp, vecs, PURGE_VEC); + return NULL; +} + static int coalesce_maps(struct vectors *vecs, vector nmpv) { @@ -384,7 +636,7 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) } orphan_paths(vecs->pathvec, mpp); - remove_map_and_stop_waiter(mpp, vecs, 1); + remove_map_and_stop_waiter(mpp, vecs); return 0; } @@ -440,7 +692,7 @@ ev_add_map (char * dev, const char * alias, struct vectors * vecs) if (mpp->wait_for_udev > 1) { condlog(2, "%s: performing delayed actions", mpp->alias); - if (update_map(mpp, vecs)) + if (update_map(mpp, vecs, 0)) /* setup multipathd removed the map */ return 1; } @@ -516,7 +768,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) } orphan_paths(vecs->pathvec, mpp); - remove_map_and_stop_waiter(mpp, vecs, 1); + remove_map_and_stop_waiter(mpp, vecs); out: lock_cleanup_pop(vecs->lock); FREE(alias); @@ -567,16 +819,17 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) if (pp) { int r; - condlog(2, "%s: spurious uevent, path already in pathvec", + condlog(3, "%s: spurious uevent, path already in pathvec", uev->kernel); if (!pp->mpp && !strlen(pp->wwid)) { condlog(3, "%s: reinitialize path", uev->kernel); udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (r == PATHINFO_OK) ret = ev_add_path(pp, vecs, need_do_map); else if (r == PATHINFO_SKIPPED) { @@ -601,9 +854,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) * get path vital state */ conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, DI_ALL, &pp); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (!pp) { if (ret == PATHINFO_SKIPPED) return 0; @@ -686,7 +940,7 @@ rescan: mpp->action = ACT_RELOAD; extract_hwe_from_path(mpp); } else { - if (!should_multipath(pp, vecs->pathvec)) { + if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) { orphan_path(pp, "only one path"); return 0; } @@ -751,6 +1005,11 @@ retry: } dm_lib_release(); + if ((mpp->action == ACT_CREATE || + (mpp->action == ACT_NOTHING && start_waiter && !mpp->waiter)) && + wait_for_events(mpp, vecs)) + goto fail_map; + /* * update our state from kernel regardless of create or reload */ @@ -759,11 +1018,6 @@ retry: sync_map_state(mpp); - if ((mpp->action == ACT_CREATE || - (mpp->action == ACT_NOTHING && start_waiter && !mpp->waiter)) && - start_waiter_thread(mpp, vecs)) - goto fail_map; - if (retries >= 0) { condlog(2, "%s [%s]: path added to devmap %s", pp->dev, pp->dev_t, mpp->alias); @@ -899,7 +1153,7 @@ out: return retval; fail: - remove_map_and_stop_waiter(mpp, vecs, 1); + remove_map_and_stop_waiter(mpp, vecs); return 1; } @@ -968,10 +1222,11 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO) != PATHINFO_OK) condlog(1, "%s: pathinfo failed after change uevent", uev->kernel); - put_multipath_config(conf); + pthread_cleanup_pop(1); } if (pp->initialized == INIT_REQUESTED_UDEV) @@ -999,8 +1254,9 @@ out: int flag = DI_SYSFS | DI_WWID; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); retval = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, flag, NULL); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (retval == PATHINFO_SKIPPED) { condlog(3, "%s: spurious uevent, path is blacklisted", uev->kernel); @@ -1355,7 +1611,7 @@ mpvec_garbage_collector (struct vectors * vecs) vector_foreach_slot (vecs->mpvec, mpp, i) { if (mpp && mpp->alias && !dm_map_present(mpp->alias)) { condlog(2, "%s: remove dead map", mpp->alias); - remove_map_and_stop_waiter(mpp, vecs, 1); + remove_map_and_stop_waiter(mpp, vecs); i--; } } @@ -1399,7 +1655,8 @@ missing_uev_wait_tick(struct vectors *vecs) if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) { timed_out = 1; condlog(0, "%s: timeout waiting on creation uevent. enabling reloads", mpp->alias); - if (mpp->wait_for_udev > 1 && update_map(mpp, vecs)) { + if (mpp->wait_for_udev > 1 && + update_map(mpp, vecs, 0)) { /* update_map removed map */ i--; continue; @@ -1431,7 +1688,7 @@ ghost_delay_tick(struct vectors *vecs) condlog(0, "%s: timed out waiting for active path", mpp->alias); mpp->force_udev_reload = 1; - if (update_map(mpp, vecs) != 0) { + if (update_map(mpp, vecs, 0) != 0) { /* update_map removed map */ i--; continue; @@ -1491,8 +1748,10 @@ int update_prio(struct path *pp, int refresh_all) vector_foreach_slot (pgp->paths, pp1, j) { oldpriority = pp1->priority; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, + conf); pathinfo(pp1, conf, DI_PRIO); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (pp1->priority != oldpriority) changed = 1; } @@ -1501,9 +1760,10 @@ int update_prio(struct path *pp, int refresh_all) } oldpriority = pp->priority; conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (pp->state != PATH_DOWN) pathinfo(pp, conf, DI_PRIO); - put_multipath_config(conf); + pthread_cleanup_pop(1); if (pp->priority == oldpriority) return 0; @@ -1590,8 +1850,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) if (newstate == PATH_UP) { conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); newstate = get_state(pp, conf, 1, newstate); - put_multipath_config(conf); + pthread_cleanup_pop(1); } else checker_clear_message(&pp->checker); @@ -1604,8 +1865,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { condlog(2, "%s: unusable path", pp->dev); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); pathinfo(pp, conf, 0); - put_multipath_config(conf); + pthread_cleanup_pop(1); return 1; } if (!pp->mpp) { @@ -1613,15 +1875,14 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) (newstate == PATH_UP || newstate == PATH_GHOST)) { condlog(2, "%s: add missing path", pp->dev); conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST); + pthread_cleanup_pop(1); if (ret == PATHINFO_OK) { ev_add_path(pp, vecs, 1); pp->tick = 1; - } else if (ret == PATHINFO_SKIPPED) { - put_multipath_config(conf); + } else if (ret == PATHINFO_SKIPPED) return -1; - } - put_multipath_config(conf); } return 0; } @@ -1855,7 +2116,6 @@ checkerloop (void *ap) struct path *pp; int count = 0; unsigned int i; - struct itimerval timer_tick_it; struct timespec last_time; struct config *conf; @@ -1873,8 +2133,7 @@ checkerloop (void *ap) while (1) { struct timespec diff_time, start_time, end_time; - int num_paths = 0, ticks = 0, signo, strict_timing, rc = 0; - sigset_t mask; + int num_paths = 0, ticks = 0, strict_timing, rc = 0; if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) start_time.tv_sec = 0; @@ -1962,25 +2221,18 @@ checkerloop (void *ap) if (!strict_timing) sleep(1); else { - timer_tick_it.it_interval.tv_sec = 0; - timer_tick_it.it_interval.tv_usec = 0; if (diff_time.tv_nsec) { - timer_tick_it.it_value.tv_sec = 0; - timer_tick_it.it_value.tv_usec = + diff_time.tv_sec = 0; + diff_time.tv_nsec = 1000UL * 1000 * 1000 - diff_time.tv_nsec; - } else { - timer_tick_it.it_value.tv_sec = 1; - timer_tick_it.it_value.tv_usec = 0; - } - setitimer(ITIMER_REAL, &timer_tick_it, NULL); + } else + diff_time.tv_sec = 1; - sigemptyset(&mask); - sigaddset(&mask, SIGALRM); condlog(3, "waiting for %lu.%06lu secs", - timer_tick_it.it_value.tv_sec, - timer_tick_it.it_value.tv_usec); - if (sigwait(&mask, &signo) != 0) { - condlog(3, "sigwait failed with error %d", + diff_time.tv_sec, + diff_time.tv_nsec / 1000); + if (nanosleep(&diff_time, NULL) != 0) { + condlog(3, "nanosleep failed with error %d", errno); conf = get_multipath_config(); conf->strict_timing = 0; @@ -2029,6 +2281,7 @@ configure (struct vectors * vecs) vector_foreach_slot (vecs->pathvec, pp, i){ conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); if (filter_path(conf, pp) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); @@ -2036,7 +2289,7 @@ configure (struct vectors * vecs) } else pp->checkint = conf->checkint; - put_multipath_config(conf); + pthread_cleanup_pop(1); } if (map_discovery(vecs)) { condlog(0, "configure failed at map discovery"); @@ -2070,7 +2323,8 @@ configure (struct vectors * vecs) sync_maps_state(mpvec); vector_foreach_slot(mpvec, mpp, i){ - remember_wwid(mpp->wwid); + if (remember_wwid(mpp->wwid) == 1) + trigger_paths_udev_change(mpp, true); update_map_pr(mpp); } @@ -2089,14 +2343,13 @@ configure (struct vectors * vecs) * start dm event waiter threads for these new maps */ vector_foreach_slot(vecs->mpvec, mpp, i) { - if (setup_multipath(vecs, mpp)) { + if (wait_for_events(mpp, vecs)) { + remove_map(mpp, vecs, 1); i--; continue; } - if (start_waiter_thread(mpp, vecs)) { - remove_map(mpp, vecs, 1); + if (setup_multipath(vecs, mpp)) i--; - } } return 0; } @@ -2146,17 +2399,11 @@ reconfigure (struct vectors * vecs) /* Re-read any timezone changes */ tzset(); - dm_drv_version(conf->version, TGT_MPATH); + dm_tgt_version(conf->version, TGT_MPATH); if (verbosity) conf->verbosity = verbosity; if (bindings_read_only) conf->bindings_read_only = bindings_read_only; - if (conf->find_multipaths) { - condlog(2, "find_multipaths is set: -n is implied"); - ignore_new_devs = 1; - } - if (ignore_new_devs) - conf->ignore_new_devs = ignore_new_devs; uxsock_timeout = conf->uxsock_timeout; old = rcu_dereference(multipath_conf); @@ -2219,9 +2466,8 @@ handle_signals(bool nonfatal) } if (log_reset_sig) { condlog(2, "reset log (signal)"); - pthread_mutex_lock(&logq_lock); - log_reset("multipathd"); - pthread_mutex_unlock(&logq_lock); + if (logsink == 1) + log_thread_reset(); } reconfig_sig = 0; log_reset_sig = 0; @@ -2337,7 +2583,7 @@ set_oom_adj (void) static int child (void * param) { - pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr; + pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr; pthread_attr_t log_attr, misc_attr, uevent_attr; struct vectors * vecs; struct multipath * mpp; @@ -2350,6 +2596,7 @@ child (void * param) int pid_fd = -1; struct config *conf; char *envp; + int queue_without_daemon; mlockall(MCL_CURRENT | MCL_FUTURE); signal_init(); @@ -2386,8 +2633,6 @@ child (void * param) conf->verbosity = verbosity; if (bindings_read_only) conf->bindings_read_only = bindings_read_only; - if (ignore_new_devs) - conf->ignore_new_devs = ignore_new_devs; uxsock_timeout = conf->uxsock_timeout; rcu_assign_pointer(multipath_conf, conf); if (init_checkers(conf->multipath_dir)) { @@ -2402,6 +2647,8 @@ child (void * param) init_foreign(conf->multipath_dir); + if (poll_dmevents) + poll_dmevents = dmevent_poll_supported(); setlogmask(LOG_UPTO(conf->verbosity + 3)); envp = getenv("LimitNOFILE"); @@ -2468,6 +2715,19 @@ child (void * param) init_path_check_interval(vecs); + if (poll_dmevents) { + if (init_dmevent_waiter(vecs)) { + condlog(0, "failed to allocate dmevents waiter info"); + goto failed; + } + if ((rc = pthread_create(&dmevent_thr, &misc_attr, + wait_dmevents, NULL))) { + condlog(0, "failed to create dmevent waiter thread: %d", + rc); + goto failed; + } + } + /* * Start uevent listener early to catch events */ @@ -2526,10 +2786,11 @@ child (void * param) lock(&vecs->lock); conf = get_multipath_config(); - if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) + queue_without_daemon = conf->queue_without_daemon; + put_multipath_config(conf); + if (queue_without_daemon == QUE_NO_DAEMON_OFF) vector_foreach_slot(vecs->mpvec, mpp, i) dm_queue_if_no_path(mpp->alias, 0); - put_multipath_config(conf); remove_maps_and_stop_waiters(vecs); unlock(&vecs->lock); @@ -2537,11 +2798,15 @@ child (void * param) pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); pthread_cancel(uevq_thr); + if (poll_dmevents) + pthread_cancel(dmevent_thr); pthread_join(check_thr, NULL); pthread_join(uevent_thr, NULL); pthread_join(uxlsnr_thr, NULL); pthread_join(uevq_thr, NULL); + if (poll_dmevents) + pthread_join(dmevent_thr, NULL); stop_io_err_stat_thread(); @@ -2557,6 +2822,8 @@ child (void * param) cleanup_foreign(); cleanup_checkers(); cleanup_prio(); + if (poll_dmevents) + cleanup_dmevent_waiter(); dm_lib_release(); dm_lib_exit(); @@ -2688,7 +2955,7 @@ main (int argc, char *argv[]) udev = udev_new(); libmp_udev_set_sync_support(0); - while ((arg = getopt(argc, argv, ":dsv:k::Bn")) != EOF ) { + while ((arg = getopt(argc, argv, ":dsv:k::Bniw")) != EOF ) { switch(arg) { case 'd': foreground = 1; @@ -2720,7 +2987,10 @@ main (int argc, char *argv[]) bindings_read_only = 1; break; case 'n': - ignore_new_devs = 1; + condlog(0, "WARNING: ignoring deprecated option -n, use 'ignore_wwids = no' instead"); + break; + case 'w': + poll_dmevents = 0; break; default: fprintf(stderr, "Invalid argument '-%c'\n", @@ -2780,12 +3050,13 @@ void * mpath_pr_event_handler_fn (void * pathp ) struct prout_param_descriptor *param; struct prin_resp *resp; + rcu_register_thread(); mpp = pp->mpp; resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA); if (!resp){ condlog(0,"%s Alloc failed for prin response", pp->dev); - return NULL; + goto out; } ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0); @@ -2843,7 +3114,9 @@ void * mpath_pr_event_handler_fn (void * pathp ) free(param); out: - free(resp); + if (resp) + free(resp); + rcu_unregister_thread(); return NULL; } @@ -2856,7 +3129,7 @@ int mpath_pr_event_handle(struct path *pp) mpp = pp->mpp; - if (get_be64(mpp->reservation_key)) + if (!get_be64(mpp->reservation_key)) return -1; pthread_attr_init(&attr); diff --git a/multipathd/main.h b/multipathd/main.h index 0e9c5e3..af39558 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -39,5 +39,9 @@ void * mpath_pr_event_handler_fn (void * ); int update_map_pr(struct multipath *mpp); void * mpath_pr_event_handler_fn (void * pathp ); void handle_signals(bool); +int __setup_multipath (struct vectors * vecs, struct multipath * mpp, + int reset); +#define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1) +int update_multipath (struct vectors *vecs, char *mapname, int reset); #endif /* MAIN_H */ diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index 5c96680..e78ac9e 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -25,7 +25,6 @@ multipathd \- Multipath daemon. .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-B \|] -.RB [\| \-n \|] . . .\" ---------------------------------------------------------------------------- @@ -73,8 +72,11 @@ be viewed by entering '\fIhelp\fR'. When you are finished entering commands, pre . .TP .B \-n -Ignore new devices. multipathd will not create a multipath device unless the -WWID for the device is already listed in the WWIDs file. +\fBIGNORED\fR. Use the option +\fIfind_multipaths\fR to control the treatment of newly detected devices by +multipathd. See +.BR multipath.conf(5). +. . . .\" ---------------------------------------------------------------------------- diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index c5c32ea..08db0e8 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -25,6 +25,7 @@ #include "vector.h" #include "cli.h" +#include "uxclnt.h" static void print_reply(char *s) { diff --git a/multipathd/waiter.c b/multipathd/waiter.c new file mode 100644 index 0000000..5895f15 --- /dev/null +++ b/multipathd/waiter.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + * Copyright (c) 2005 Kiyoshi Ueda, NEC + * Copyright (c) 2005 Benjamin Marzinski, Redhat + * Copyright (c) 2005 Edward Goggin, EMC + */ +#include +#include +#include +#include +#include +#include + +#include "vector.h" +#include "memory.h" +#include "checkers.h" +#include "config.h" +#include "structs.h" +#include "structs_vec.h" +#include "devmapper.h" +#include "debug.h" +#include "lock.h" +#include "waiter.h" +#include "main.h" + +pthread_attr_t waiter_attr; +struct mutex_lock waiter_lock = { .mutex = PTHREAD_MUTEX_INITIALIZER }; + +static struct event_thread *alloc_waiter (void) +{ + + struct event_thread *wp; + + wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); + memset(wp, 0, sizeof(struct event_thread)); + + return wp; +} + +static void free_waiter (void *data) +{ + struct event_thread *wp = (struct event_thread *)data; + + if (wp->dmt) + dm_task_destroy(wp->dmt); + + rcu_unregister_thread(); + FREE(wp); +} + +void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs) +{ + pthread_t thread; + + if (mpp->waiter == (pthread_t)0) { + condlog(3, "%s: event checker thread already stopped", + mpp->alias); + return; + } + /* Don't cancel yourself. __setup_multipath is called by + by the waiter thread, and may remove a multipath device */ + if (pthread_equal(mpp->waiter, pthread_self())) + return; + + condlog(3, "%s: stop event checker thread (%lu)", mpp->alias, + mpp->waiter); + thread = mpp->waiter; + mpp->waiter = (pthread_t)0; + pthread_cleanup_push(cleanup_lock, &waiter_lock); + lock(&waiter_lock); + pthread_kill(thread, SIGUSR2); + pthread_cancel(thread); + lock_cleanup_pop(&waiter_lock); +} + +/* + * returns the reschedule delay + * negative means *stop* + */ +static int waiteventloop (struct event_thread *waiter) +{ + sigset_t set, oldset; + int event_nr; + int r; + + if (!waiter->event_nr) + waiter->event_nr = dm_geteventnr(waiter->mapname); + + if (!(waiter->dmt = libmp_dm_task_create(DM_DEVICE_WAITEVENT))) { + condlog(0, "%s: devmap event #%i dm_task_create error", + waiter->mapname, waiter->event_nr); + return 1; + } + + if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { + condlog(0, "%s: devmap event #%i dm_task_set_name error", + waiter->mapname, waiter->event_nr); + dm_task_destroy(waiter->dmt); + waiter->dmt = NULL; + return 1; + } + + if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, + waiter->event_nr)) { + condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", + waiter->mapname, waiter->event_nr); + dm_task_destroy(waiter->dmt); + waiter->dmt = NULL; + return 1; + } + + dm_task_no_open_count(waiter->dmt); + + /* wait */ + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + pthread_sigmask(SIG_UNBLOCK, &set, &oldset); + + pthread_testcancel(); + r = dm_task_run(waiter->dmt); + pthread_testcancel(); + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + dm_task_destroy(waiter->dmt); + waiter->dmt = NULL; + + if (!r) { /* wait interrupted by signal. check for cancellation */ + pthread_cleanup_push(cleanup_lock, &waiter_lock); + lock(&waiter_lock); + pthread_testcancel(); + lock_cleanup_pop(&waiter_lock); + return 1; /* If we weren't cancelled, just reschedule */ + } + + waiter->event_nr++; + + /* + * upon event ... + */ + while (1) { + condlog(3, "%s: devmap event #%i", + waiter->mapname, waiter->event_nr); + + /* + * event might be : + * + * 1) a table reload, which means our mpp structure is + * obsolete : refresh it through update_multipath() + * 2) a path failed by DM : mark as such through + * update_multipath() + * 3) map has gone away : stop the thread. + * 4) a path reinstate : nothing to do + * 5) a switch group : nothing to do + */ + pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock); + lock(&waiter->vecs->lock); + pthread_testcancel(); + r = update_multipath(waiter->vecs, waiter->mapname, 1); + lock_cleanup_pop(waiter->vecs->lock); + + if (r) { + condlog(2, "%s: event checker exit", + waiter->mapname); + return -1; /* stop the thread */ + } + + event_nr = dm_geteventnr(waiter->mapname); + + if (waiter->event_nr == event_nr) + return 1; /* upon problem reschedule 1s later */ + + waiter->event_nr = event_nr; + } + return -1; /* never reach there */ +} + +static void *waitevent (void *et) +{ + int r; + struct event_thread *waiter; + + mlockall(MCL_CURRENT | MCL_FUTURE); + + waiter = (struct event_thread *)et; + pthread_cleanup_push(free_waiter, et); + + rcu_register_thread(); + while (1) { + r = waiteventloop(waiter); + + if (r < 0) + break; + + sleep(r); + } + + pthread_cleanup_pop(1); + return NULL; +} + +int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) +{ + struct event_thread *wp; + + if (!mpp) + return 0; + + wp = alloc_waiter(); + + if (!wp) + goto out; + + strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1); + wp->vecs = vecs; + + if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { + condlog(0, "%s: cannot create event checker", wp->mapname); + goto out1; + } + mpp->waiter = wp->thread; + condlog(3, "%s: event checker started", wp->mapname); + + return 0; +out1: + free_waiter(wp); + mpp->waiter = (pthread_t)0; +out: + condlog(0, "failed to start waiter thread"); + return 1; +} diff --git a/multipathd/waiter.h b/multipathd/waiter.h new file mode 100644 index 0000000..0cfae46 --- /dev/null +++ b/multipathd/waiter.h @@ -0,0 +1,17 @@ +#ifndef _WAITER_H +#define _WAITER_H + +extern pthread_attr_t waiter_attr; + +struct event_thread { + struct dm_task *dmt; + pthread_t thread; + int event_nr; + char mapname[WWID_SIZE]; + struct vectors *vecs; +}; + +void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs); +int start_waiter_thread (struct multipath *mpp, struct vectors *vecs); + +#endif /* _WAITER_H */ diff --git a/tests/Makefile b/tests/Makefile index 81f5518..1f36411 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,11 +3,16 @@ include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka -TESTS := uevent parser +TESTS := uevent parser util dmevents .SILENT: $(TESTS:%=%.o) .PRECIOUS: $(TESTS:%=%-test) +all: $(TESTS:%=%.out) + +dmevents-test: dmevents.o ../multipathd/dmevents.c globals.c $(multipathdir)/libmultipath.so + @$(CC) -o $@ $< $(LDFLAGS) $(LIBDEPS) -lpthread -ldevmapper -lurcu -Wl,--wrap=open -Wl,--wrap=close -Wl,--wrap=dm_is_mpath -Wl,--wrap=dm_geteventnr -Wl,--wrap=ioctl -Wl,--wrap=libmp_dm_task_create -Wl,--wrap=dm_task_no_open_count -Wl,--wrap=dm_task_run -Wl,--wrap=dm_task_get_names -Wl,--wrap=dm_task_destroy -Wl,--wrap=poll -Wl,--wrap=remove_map_by_alias -Wl,--wrap=update_multipath + %-test: %.o globals.c $(multipathdir)/libmultipath.so @$(CC) -o $@ $< $(LDFLAGS) $(LIBDEPS) @@ -15,8 +20,6 @@ TESTS := uevent parser @echo == running $< == @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@ -all: $(TESTS:%=%.out) - clean: dep_clean rm -f $(TESTS:%=%-test) $(TESTS:%=%.out) $(TESTS:%=%.o) diff --git a/tests/dmevents.c b/tests/dmevents.c new file mode 100644 index 0000000..bba51dc --- /dev/null +++ b/tests/dmevents.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2018 Benjamin Marzinski, Redhat + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "structs.h" +#include "structs_vec.h" + +#include "globals.c" +/* I have to do this to get at the static variables */ +#include "../multipathd/dmevents.c" + +/* pretend dm device */ +struct dm_device { + char name[WWID_SIZE]; + /* is this a mpath device, or other dm device */ + int is_mpath; + uint32_t evt_nr; + /* tracks the event number when the multipath device was updated */ + uint32_t update_nr; +}; + +struct test_data { + struct vectors vecs; + vector dm_devices; + struct dm_names *names; +}; + +struct test_data data; + +/* Add a pretend dm device, or update its event number. This is used to build + * up the dm devices that the dmevents code queries with dm_task_get_names, + * dm_geteventnr, and dm_is_mpath */ +int add_dm_device_event(char *name, int is_mpath, uint32_t evt_nr) +{ + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) { + if (strcmp(name, dev->name) == 0) { + dev->evt_nr = evt_nr; + return 0; + } + } + dev = (struct dm_device *)malloc(sizeof(struct dm_device)); + if (!dev){ + condlog(0, "Testing error mallocing dm_device"); + return -1; + } + strncpy(dev->name, name, WWID_SIZE); + dev->name[WWID_SIZE - 1] = 0; + dev->is_mpath = is_mpath; + dev->evt_nr = evt_nr; + if (!vector_alloc_slot(data.dm_devices)) { + condlog(0, "Testing error setting dm_devices slot"); + free(dev); + return -1; + } + vector_set_slot(data.dm_devices, dev); + return 0; +} + +/* helper function for pretend dm devices */ +struct dm_device *find_dm_device(const char *name) +{ + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) + if (strcmp(name, dev->name) == 0) + return dev; + return NULL; +} + +/* helper function for pretend dm devices */ +int remove_dm_device_event(const char *name) +{ + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) { + if (strcmp(name, dev->name) == 0) { + vector_del_slot(data.dm_devices, i); + free(dev); + return 0; + } + } + return -1; +} + +/* helper function for pretend dm devices */ +void remove_all_dm_device_events(void) +{ + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) + free(dev); + vector_reset(data.dm_devices); +} + +static inline size_t align_val(size_t val) +{ + return (val + 7) & ~7; +} +static inline void *align_ptr(void *ptr) +{ + return (void *)align_val((size_t)ptr); +} + +/* copied off of list_devices in dm-ioctl.c except that it uses + * the pretend dm devices, and saves the output to the test_data + * structure */ +struct dm_names *build_dm_names(void) +{ + struct dm_names *names, *np, *old_np = NULL; + uint32_t *event_nr; + struct dm_device *dev; + int i, size = 0; + + if (VECTOR_SIZE(data.dm_devices) == 0) { + names = (struct dm_names *)malloc(sizeof(struct dm_names)); + if (!names) { + condlog(0, "Testing error allocating empty dm_names"); + return NULL; + } + names->dev = 0; + names->next = 0; + return names; + } + vector_foreach_slot(data.dm_devices, dev, i) { + size += align_val(offsetof(struct dm_names, name) + + strlen(dev->name) + 1); + size += align_val(sizeof(uint32_t)); + } + names = (struct dm_names *)malloc(size); + if (!names) { + condlog(0, "Testing error allocating dm_names"); + return NULL; + } + np = names; + vector_foreach_slot(data.dm_devices, dev, i) { + if (old_np) + old_np->next = (uint32_t) ((uintptr_t) np - + (uintptr_t) old_np); + np->dev = 1; + np->next = 0; + strcpy(np->name, dev->name); + + old_np = np; + event_nr = align_ptr(np->name + strlen(dev->name) + 1); + *event_nr = dev->evt_nr; + np = align_ptr(event_nr + 1); + } + assert_int_equal((char *)np - (char *)names, size); + return names; +} + +static int setup(void **state) +{ + if (dmevent_poll_supported()) { + data.dm_devices = vector_alloc(); + *state = &data; + } else + *state = NULL; + return 0; +} + +static int teardown(void **state) +{ + struct dm_device *dev; + int i; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + return 0; + vector_foreach_slot(datap->dm_devices, dev, i) + free(dev); + vector_free(datap->dm_devices); + datap = NULL; + return 0; +} + +int __wrap_open(const char *pathname, int flags) +{ + assert_ptr_equal(pathname, "/dev/mapper/control"); + assert_int_equal(flags, O_RDWR); + return mock_type(int); +} + +/* We never check the result of the close(), so there's no need to + * to mock a return value */ +int __wrap_close(int fd) +{ + assert_int_equal(fd, waiter->fd); + return 0; +} + +/* the pretend dm device code checks the input and supplies the + * return value, so there's no need to do that here */ +int __wrap_dm_is_mpath(const char *name) +{ + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) + if (strcmp(name, dev->name) == 0) + return dev->is_mpath; + return 0; +} + +/* either get return info from the pretend dm device, or + * override it to test -1 return */ +int __wrap_dm_geteventnr(const char *name) +{ + struct dm_device *dev; + int fail = mock_type(int); + + if (fail) + return -1; + dev = find_dm_device(name); + if (dev) { + /* simulate updating device state after adding it */ + dev->update_nr = dev->evt_nr; + return dev->evt_nr; + } + return -1; +} + +int __wrap_ioctl(int fd, unsigned long request, void *argp) +{ + assert_int_equal(fd, waiter->fd); + assert_int_equal(request, DM_DEV_ARM_POLL); + return mock_type(int); +} + +struct dm_task *__wrap_libmp_dm_task_create(int task) +{ + assert_int_equal(task, DM_DEVICE_LIST); + return mock_type(struct dm_task *); +} + +int __wrap_dm_task_no_open_count(struct dm_task *dmt) +{ + assert_ptr_equal((struct test_data *)dmt, &data); + return mock_type(int); +} + +int __wrap_dm_task_run(struct dm_task *dmt) +{ + assert_ptr_equal((struct test_data *)dmt, &data); + return mock_type(int); +} + +/* either get return info from the pretend dm device, or + * override it to test NULL return */ +struct dm_names * __wrap_dm_task_get_names(struct dm_task *dmt) +{ + int good = mock_type(int); + assert_ptr_equal((struct test_data *)dmt, &data); + + if (data.names) { + condlog(0, "Testing error. data.names already allocated"); + return NULL; + } + if (!good) + return NULL; + data.names = build_dm_names(); + return data.names; +} + +void __wrap_dm_task_destroy(struct dm_task *dmt) +{ + assert_ptr_equal((struct test_data *)dmt, &data); + + if (data.names) { + free(data.names); + data.names = NULL; + } +} + +int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + assert_int_equal(nfds, 1); + assert_int_equal(timeout, -1); + assert_int_equal(fds->fd, waiter->fd); + assert_int_equal(fds->events, POLLIN); + return mock_type(int); +} + +void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs, + int purge_vec) +{ + check_expected(alias); + assert_ptr_equal(vecs, waiter->vecs); + assert_int_equal(purge_vec, 1); +} + +/* pretend update the pretend dm devices. If fail is set, it + * simulates having the dm device removed. Otherwise it just sets + * update_nr to record when the update happened */ +int __wrap_update_multipath(struct vectors *vecs, char *mapname, int reset) +{ + int fail; + + check_expected(mapname); + assert_ptr_equal(vecs, waiter->vecs); + assert_int_equal(reset, 1); + fail = mock_type(int); + if (fail) { + assert_int_equal(remove_dm_device_event(mapname), 0); + return fail; + } else { + struct dm_device *dev; + int i; + + vector_foreach_slot(data.dm_devices, dev, i) { + if (strcmp(mapname, dev->name) == 0) { + dev->update_nr = dev->evt_nr; + return 0; + } + } + fail(); + } + return fail; +} + +/* helper function used to check if the dmevents list of devices + * includes a specific device. To make sure that dmevents is + * in the correct state after running a function */ +struct dev_event *find_dmevents(const char *name) +{ + struct dev_event *dev_evt; + int i; + + vector_foreach_slot(waiter->events, dev_evt, i) + if (!strcmp(dev_evt->name, name)) + return dev_evt; + return NULL; +} + +/* null vecs pointer when initialized dmevents */ +static void test_init_waiter_bad0(void **state) +{ + /* this boilerplate code just skips the test if + * dmevents polling is not supported */ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + assert_int_equal(init_dmevent_waiter(NULL), -1); +} + +/* fail to open /dev/mapper/control */ +static void test_init_waiter_bad1(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + will_return(__wrap_open, -1); + assert_int_equal(init_dmevent_waiter(&datap->vecs), -1); + assert_ptr_equal(waiter, NULL); +} + +/* waiter remains initialized after this test */ +static void test_init_waiter_good0(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + will_return(__wrap_open, 2); + assert_int_equal(init_dmevent_waiter(&datap->vecs), 0); + assert_ptr_not_equal(waiter, NULL); +} + +/* No dm device named foo */ +static void test_watch_dmevents_bad0(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + assert_int_equal(watch_dmevents("foo"), -1); + assert_ptr_equal(find_dmevents("foo"), NULL); +} + +/* foo is not a multipath device */ +static void test_watch_dmevents_bad1(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + assert_int_equal(add_dm_device_event("foo", 0, 5), 0); + assert_int_equal(watch_dmevents("foo"), -1); + assert_ptr_equal(find_dmevents("foo"), NULL); +} + +/* failed getting the dmevent number */ +static void test_watch_dmevents_bad2(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + will_return(__wrap_dm_geteventnr, -1); + assert_int_equal(watch_dmevents("foo"), -1); + assert_ptr_equal(find_dmevents("foo"), NULL); +} + +/* verify that you can watch and unwatch dm multipath device "foo" */ +static void test_watch_dmevents_good0(void **state) +{ + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + /* verify foo is being watched */ + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + assert_int_equal(VECTOR_SIZE(waiter->events), 1); + unwatch_dmevents("foo"); + /* verify foo is no longer being watched */ + assert_int_equal(VECTOR_SIZE(waiter->events), 0); + assert_ptr_equal(find_dmevents("foo"), NULL); +} + +/* verify that if you try to watch foo multiple times, it only + * is placed on the waiter list once */ +static void test_watch_dmevents_good1(void **state) +{ + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + assert_int_equal(add_dm_device_event("foo", 1, 6), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 6); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + assert_int_equal(VECTOR_SIZE(waiter->events), 1); + unwatch_dmevents("foo"); + assert_int_equal(VECTOR_SIZE(waiter->events), 0); + assert_ptr_equal(find_dmevents("foo"), NULL); +} + +/* watch and then unwatch multiple devices */ +static void test_watch_dmevents_good2(void **state) +{ + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + unwatch_all_dmevents(); + remove_all_dm_device_events(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + assert_int_equal(add_dm_device_event("bar", 1, 7), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + assert_ptr_equal(find_dmevents("bar"), NULL); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("bar"), 0); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev_evt = find_dmevents("bar"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 7); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + assert_int_equal(VECTOR_SIZE(waiter->events), 2); + unwatch_all_dmevents(); + assert_int_equal(VECTOR_SIZE(waiter->events), 0); + assert_ptr_equal(find_dmevents("foo"), NULL); + assert_ptr_equal(find_dmevents("bar"), NULL); +} + +/* dm_task_create fails */ +static void test_get_events_bad0(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + unwatch_all_dmevents(); + remove_all_dm_device_events(); + + will_return(__wrap_libmp_dm_task_create, NULL); + assert_int_equal(dm_get_events(), -1); +} + +/* dm_task_run fails */ +static void test_get_events_bad1(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 0); + assert_int_equal(dm_get_events(), -1); +} + +/* dm_task_get_names fails */ +static void test_get_events_bad2(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 0); + assert_int_equal(dm_get_events(), -1); +} + +/* If the device isn't being watched, dm_get_events returns NULL */ +static void test_get_events_good0(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + assert_int_equal(dm_get_events(), 0); + assert_ptr_equal(find_dmevents("foo"), NULL); + assert_int_equal(VECTOR_SIZE(waiter->events), 0); +} + +/* There are 5 dm devices. 4 of them are multipath devices. + * Only 3 of them are being watched. "foo" has a new event + * "xyzzy" gets removed. Nothing happens to bar. Verify + * that all the events are properly set, and that nothing + * happens with the two devices that aren't being watched */ +static void test_get_events_good1(void **state) +{ + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + assert_int_equal(add_dm_device_event("bar", 1, 7), 0); + assert_int_equal(add_dm_device_event("baz", 1, 12), 0); + assert_int_equal(add_dm_device_event("qux", 0, 4), 0); + assert_int_equal(add_dm_device_event("xyzzy", 1, 8), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("bar"), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("xyzzy"), 0); + assert_int_equal(add_dm_device_event("foo", 1, 6), 0); + assert_int_equal(remove_dm_device_event("xyzzy"), 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + assert_int_equal(dm_get_events(), 0); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 6); + assert_int_equal(dev_evt->action, EVENT_UPDATE); + dev_evt = find_dmevents("bar"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 7); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev_evt = find_dmevents("xyzzy"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 8); + assert_int_equal(dev_evt->action, EVENT_REMOVE); + assert_ptr_equal(find_dmevents("baz"), NULL); + assert_ptr_equal(find_dmevents("qux"), NULL); + assert_int_equal(VECTOR_SIZE(waiter->events), 3); +} + +/* poll does not return an event. nothing happens. The + * devices remain after this test */ +static void test_dmevent_loop_bad0(void **state) +{ + struct dm_device *dev; + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + unwatch_all_dmevents(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + assert_int_equal(add_dm_device_event("foo", 1, 6), 0); + will_return(__wrap_poll, 0); + assert_int_equal(dmevent_loop(), 1); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("foo"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 6); + assert_int_equal(dev->update_nr, 5); +} + +/* arm_dm_event_poll's ioctl fails. Nothing happens */ +static void test_dmevent_loop_bad1(void **state) +{ + struct dm_device *dev; + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, -1); + assert_int_equal(dmevent_loop(), 1); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("foo"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 6); + assert_int_equal(dev->update_nr, 5); +} + +/* dm_get_events fails. Nothing happens */ +static void test_dmevent_loop_bad2(void **state) +{ + struct dm_device *dev; + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, 0); + will_return(__wrap_libmp_dm_task_create, NULL); + assert_int_equal(dmevent_loop(), 1); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 5); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("foo"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 6); + assert_int_equal(dev->update_nr, 5); +} + +/* verify dmevent_loop runs successfully when no devices are being + * watched */ +static void test_dmevent_loop_good0(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + unwatch_all_dmevents(); + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + assert_int_equal(dmevent_loop(), 1); +} + +/* Watch 3 devices, where one device has an event (foo), one device is + * removed (xyzzy), and one device does nothing (bar). Verify that + * the device with the event gets updated, the device that is removed + * gets unwatched, and the device with no events stays the same. + * The devices remain after this test */ +static void test_dmevent_loop_good1(void **state) +{ + struct dm_device *dev; + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + remove_all_dm_device_events(); + unwatch_all_dmevents(); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + assert_int_equal(add_dm_device_event("bar", 1, 7), 0); + assert_int_equal(add_dm_device_event("baz", 1, 12), 0); + assert_int_equal(add_dm_device_event("xyzzy", 1, 8), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("foo"), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("bar"), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("xyzzy"), 0); + assert_int_equal(add_dm_device_event("foo", 1, 6), 0); + assert_int_equal(remove_dm_device_event("xyzzy"), 0); + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + expect_string(__wrap_update_multipath, mapname, "foo"); + will_return(__wrap_update_multipath, 0); + expect_string(__wrap_remove_map_by_alias, alias, "xyzzy"); + assert_int_equal(dmevent_loop(), 1); + assert_int_equal(VECTOR_SIZE(waiter->events), 2); + assert_int_equal(VECTOR_SIZE(data.dm_devices), 3); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 6); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("foo"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 6); + assert_int_equal(dev->update_nr, 6); + dev_evt = find_dmevents("bar"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 7); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("bar"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 7); + assert_int_equal(dev->update_nr, 7); + assert_ptr_equal(find_dmevents("xyzzy"), NULL); + assert_ptr_equal(find_dm_device("xyzzy"), NULL); +} + +/* watch another dm device and add events to two of them, so bar and + * baz have new events, and foo doesn't. Set update_multipath to + * fail for baz. Verify that baz is unwatched, bar is updated, and + * foo stays the same. */ +static void test_dmevent_loop_good2(void **state) +{ + struct dm_device *dev; + struct dev_event *dev_evt; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + assert_int_equal(add_dm_device_event("bar", 1, 9), 0); + will_return(__wrap_dm_geteventnr, 0); + assert_int_equal(watch_dmevents("baz"), 0); + assert_int_equal(add_dm_device_event("baz", 1, 14), 0); + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + expect_string(__wrap_update_multipath, mapname, "bar"); + will_return(__wrap_update_multipath, 0); + expect_string(__wrap_update_multipath, mapname, "baz"); + will_return(__wrap_update_multipath, 1); + assert_int_equal(dmevent_loop(), 1); + assert_int_equal(VECTOR_SIZE(waiter->events), 2); + assert_int_equal(VECTOR_SIZE(data.dm_devices), 2); + dev_evt = find_dmevents("foo"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 6); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("foo"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 6); + assert_int_equal(dev->update_nr, 6); + dev_evt = find_dmevents("bar"); + assert_ptr_not_equal(dev_evt, NULL); + assert_int_equal(dev_evt->evt_nr, 9); + assert_int_equal(dev_evt->action, EVENT_NOTHING); + dev = find_dm_device("bar"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 9); + assert_int_equal(dev->update_nr, 9); + assert_ptr_equal(find_dmevents("baz"), NULL); + assert_ptr_equal(find_dm_device("baz"), NULL); +} + +/* remove dm device foo, and unwatch events on bar. Verify that + * foo is cleaned up and unwatched, and bar is no longer updated */ +static void test_dmevent_loop_good3(void **state) +{ + struct dm_device *dev; + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + + assert_int_equal(remove_dm_device_event("foo"), 0); + unwatch_dmevents("bar"); + will_return(__wrap_poll, 1); + will_return(__wrap_ioctl, 0); + will_return(__wrap_libmp_dm_task_create, &data); + will_return(__wrap_dm_task_no_open_count, 1); + will_return(__wrap_dm_task_run, 1); + will_return(__wrap_dm_task_get_names, 1); + expect_string(__wrap_remove_map_by_alias, alias, "foo"); + assert_int_equal(dmevent_loop(), 1); + assert_int_equal(VECTOR_SIZE(waiter->events), 0); + assert_int_equal(VECTOR_SIZE(data.dm_devices), 1); + dev = find_dm_device("bar"); + assert_ptr_not_equal(dev, NULL); + assert_int_equal(dev->evt_nr, 9); + assert_int_equal(dev->update_nr, 9); + assert_ptr_equal(find_dmevents("foo"), NULL); + assert_ptr_equal(find_dmevents("bar"), NULL); + assert_ptr_equal(find_dm_device("foo"), NULL); +} + + +/* verify that rearming the dmevents polling works */ +static void test_arm_poll(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + will_return(__wrap_ioctl, 0); + assert_int_equal(arm_dm_event_poll(waiter->fd), 0); +} + +/* verify that the waiter is cleaned up */ +static void test_cleanup_waiter(void **state) +{ + struct test_data *datap = (struct test_data *)(*state); + if (datap == NULL) + skip(); + cleanup_dmevent_waiter(); + assert_ptr_equal(waiter, NULL); +} + +int test_dmevents(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_init_waiter_bad0), + cmocka_unit_test(test_init_waiter_bad1), + cmocka_unit_test(test_init_waiter_good0), + cmocka_unit_test(test_watch_dmevents_bad0), + cmocka_unit_test(test_watch_dmevents_bad1), + cmocka_unit_test(test_watch_dmevents_bad2), + cmocka_unit_test(test_watch_dmevents_good0), + cmocka_unit_test(test_watch_dmevents_good1), + cmocka_unit_test(test_watch_dmevents_good2), + cmocka_unit_test(test_get_events_bad0), + cmocka_unit_test(test_get_events_bad1), + cmocka_unit_test(test_get_events_bad2), + cmocka_unit_test(test_get_events_good0), + cmocka_unit_test(test_get_events_good1), + cmocka_unit_test(test_arm_poll), + cmocka_unit_test(test_dmevent_loop_bad0), + cmocka_unit_test(test_dmevent_loop_bad1), + cmocka_unit_test(test_dmevent_loop_bad2), + cmocka_unit_test(test_dmevent_loop_good0), + cmocka_unit_test(test_dmevent_loop_good1), + cmocka_unit_test(test_dmevent_loop_good2), + cmocka_unit_test(test_dmevent_loop_good3), + cmocka_unit_test(test_cleanup_waiter), + }; + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + + ret += test_dmevents(); + return ret; +} diff --git a/tests/globals.c b/tests/globals.c index 80f57bd..07d970e 100644 --- a/tests/globals.c +++ b/tests/globals.c @@ -14,5 +14,5 @@ struct config *get_multipath_config(void) return &conf; } -void put_multipath_config(struct config* c) +void put_multipath_config(void *arg) {} diff --git a/tests/util.c b/tests/util.c new file mode 100644 index 0000000..113b134 --- /dev/null +++ b/tests/util.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018 Benjamin Marzinski, Redhat + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include "util.h" + +#include "globals.c" + +static void test_basenamecpy_good0(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("foobar", dst, sizeof(dst)), 6); + assert_string_equal(dst, "foobar"); +} + +static void test_basenamecpy_good1(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("foo/bar", dst, sizeof(dst)), 3); + assert_string_equal(dst, "bar"); +} + +static void test_basenamecpy_good2(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("/thud/blat", dst, sizeof(dst)), 4); + assert_string_equal(dst, "blat"); +} + +static void test_basenamecpy_good3(void **state) +{ + char dst[4]; + + assert_int_equal(basenamecpy("foo/bar", dst, sizeof(dst)), 3); + assert_string_equal(dst, "bar"); +} + +static void test_basenamecpy_good4(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("/xyzzy", dst, sizeof(dst)), 5); + assert_string_equal(dst, "xyzzy"); +} + +static void test_basenamecpy_good5(void **state) +{ + char dst[4]; + + assert_int_equal(basenamecpy("/foo/bar\n", dst, sizeof(dst)), 3); + assert_string_equal(dst, "bar"); +} + +/* multipath expects any trailing whitespace to be stripped off the basename, + * so that it will match pp->dev */ +static void test_basenamecpy_good6(void **state) +{ + char dst[6]; + + assert_int_equal(basenamecpy("/xyzzy/plugh ", dst, sizeof(dst)), 5); + assert_string_equal(dst, "plugh"); +} + +static void test_basenamecpy_good7(void **state) +{ + char src[] = "/foo/bar"; + char dst[10]; + + assert_int_equal(basenamecpy(src, dst, sizeof(dst)), 3); + + strcpy(src, "badbadno"); + assert_string_equal(dst, "bar"); +} + +/* buffer too small */ +static void test_basenamecpy_bad0(void **state) +{ + char dst[3]; + + assert_int_equal(basenamecpy("baz", dst, sizeof(dst)), 0); +} + +/* ends in slash */ +static void test_basenamecpy_bad1(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("foo/bar/", dst, sizeof(dst)), 0); +} + +static void test_basenamecpy_bad2(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy(NULL, dst, sizeof(dst)), 0); +} + +static void test_basenamecpy_bad3(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("", dst, sizeof(dst)), 0); +} + +static void test_basenamecpy_bad4(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("/", dst, sizeof(dst)), 0); +} + +static void test_basenamecpy_bad5(void **state) +{ + char dst[10]; + + assert_int_equal(basenamecpy("baz/qux", NULL, sizeof(dst)), 0); +} + +int test_basenamecpy(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_basenamecpy_good0), + cmocka_unit_test(test_basenamecpy_good1), + cmocka_unit_test(test_basenamecpy_good2), + cmocka_unit_test(test_basenamecpy_good3), + cmocka_unit_test(test_basenamecpy_good4), + cmocka_unit_test(test_basenamecpy_good5), + cmocka_unit_test(test_basenamecpy_good6), + cmocka_unit_test(test_basenamecpy_good7), + cmocka_unit_test(test_basenamecpy_bad0), + cmocka_unit_test(test_basenamecpy_bad1), + cmocka_unit_test(test_basenamecpy_bad2), + cmocka_unit_test(test_basenamecpy_bad3), + cmocka_unit_test(test_basenamecpy_bad4), + cmocka_unit_test(test_basenamecpy_bad5), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} + +int main(void) +{ + int ret = 0; + + ret += test_basenamecpy(); + return ret; +}