pax_global_header00006660000000000000000000000064126077132730014522gustar00rootroot0000000000000052 comment=656f8865e7434d1fe9ed4727ad498664fda0b86a multipath-tools-0.5.0+git1.656f8865/000077500000000000000000000000001260771327300165515ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/.gitignore000066400000000000000000000001741260771327300205430ustar00rootroot00000000000000*.o .dotest *~ *.so *.so.0 *.a *.gz kpartx/kpartx multipath/multipath multipathd/multipathd mpathpersist/mpathpersist .nfs* multipath-tools-0.5.0+git1.656f8865/AUTHOR000066400000000000000000000000651260771327300174770ustar00rootroot00000000000000Christophe Varoqui, multipath-tools-0.5.0+git1.656f8865/COPYING000066400000000000000000000613071260771327300176130ustar00rootroot00000000000000 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 Appendix: 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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! multipath-tools-0.5.0+git1.656f8865/ChangeLog000066400000000000000000000003031260771327300203170ustar00rootroot00000000000000Change logs are at : - pre-0.4.5 http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog - post-0.4.5 http://www.kernel.org/git/gitweb.cgi?p=linux/storage/multipath-tools/.git;a=log multipath-tools-0.5.0+git1.656f8865/FAQ000066400000000000000000000071301260771327300171040ustar00rootroot00000000000000More at http://christophe.varoqui.free.fr/faq.html See also http://christophe.varoqui.free.fr/usage.html 1. How to set up System-on-multipath ? ====================================== prerequisite : udev and multipath-tools installed here are the steps on a Debian SID system : * add dm-mpath and dm-multipath to /etc/mkinitrd/modules * copy $tools_dir/multipath/0[12]_* to /etc/mkinitrd/scripts * define a friendly alias for your multipathed root disk (in /etc/multipath.conf). Example : "system" * enable busybox in /etc/mkinitrd/mkinitrd.conf and set ROOT to any valid block-device (but not a /dev/dm-* one, due to an mkintrd misbelief that all dm-* are managed by LVM2) * run mkinitrd * in /boot/grub/menu.lst, define the root= kernel parameter Example : root=/dev/system1 * modify /etc/fstab to reference /dev/system* partitions At reboot, you should see some info like : path /dev/sda : multipath system ... gpt: 0 slices dos: 5 slices reduced size of partition #2 to 63 Added system1 : 0 70685937 linear /dev/system 63 Added system2 : 0 63 linear /dev/system 7068600 Added system5 : 0 995967 linear /dev/system 70686063 ... 2. How does it compare to commercial product XXX ? ================================================== Here are a few distinctive features : * you can mix HBA models, even different vendors, different speed ... * you can mix storage controllers on your SAN, and access them all, applying different path grouping policy * completely event-driven model : no administration burden if you accept the default behaviours * extensible : you can plug your own policies if the available ones don't fill your needs * supports root FS on multipathed SAN * free, open-source software 3. LVM2 doesn't see my multipathed devices as PV, what's up ? ============================================================= By default, lvm2 does not consider device-mapper block devices (such as a dm-crypt device) for use as physical volumes. In order to use a dm-crypt device as an lvm2 pv, add this line to the devices block in /etc/lvm/lvm.conf: types = [ "device-mapper", 16 ] If /etc/lvm/lvm.conf does not exist, you can create one based on your current/default config like so: lvm dumpconfig > /etc/lvm/lvm.conf (tip from Christophe Saout) 4. I see a lot of "io_setup failed" message using the directio checker ====================================================================== The directio path checker makes use of the asynchronous I/O API (aio) provided by modern Linux systems. Asynchronous I/O allows an application to submit I/O requests asynchronously and be notified later of their completion status. To allow this, we must allocate an asynchronous I/O context (an object of type aio_context_t) and this task is handled by the io_setup system call. A system wide limit on the number of AIO contexts that may be active simultaneously is imposed via the aio-max-nr sysctl parameter. Once this limit has been reached further calls to io_setup will fail with the error number EAGAIN leading to the "io_setup failed" messages seen for e.g. when running "multipath -ll". To avoid this problem the number of available aio contexts should be increased by setting the aio-max-nr parameter. This can be set on a one-time basis via the /proc file system, for e.g.: # echo 131072 > /proc/sys/fs/aio-max-nr Doubles the number of available contexts from the default value of 65536. To make this setting persistent a line may be added to /etc/sysctl.conf: fs.aio-max-nr = 131072 Consult appropriate application and operating system tuning recommendations for guidance on appropriate values for this parameter. multipath-tools-0.5.0+git1.656f8865/Makefile000066400000000000000000000030021260771327300202040ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # # # Try to supply the linux kernel headers. # ifeq ($(KRNLSRC),) KRNLLIB = /lib/modules/$(shell uname -r) ifeq ($(shell test -r $(KRNLLIB)/source && echo 1),1) KRNLSRC = $(KRNLLIB)/source KRNLOBJ = $(KRNLLIB)/build else KRNLSRC = $(KRNLLIB)/build KRNLOBJ = $(KRNLLIB)/build endif endif export KRNLSRC export KRNLOBJ BUILDDIRS = \ libmultipath \ libmultipath/prioritizers \ libmultipath/checkers \ libmpathpersist \ multipath \ multipathd \ mpathpersist \ kpartx ifeq ($(MULTIPATH_VERSION),) VERSION = $(shell basename ${PWD} | cut -d'-' -f3) else VERSION = $(MULTIPATH_VERSION) endif all: recurse recurse: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir BUILD=$(BUILD) VERSION=$(VERSION) \ KRNLSRC=$(KRNLSRC) KRNLOBJ=$(KRNLOBJ) || exit $?; \ done recurse_clean: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir clean || exit $?; \ done recurse_install: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir install || exit $?; \ done recurse_uninstall: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir uninstall || exit $?; \ done clean: recurse_clean rm -f multipath-tools.spec rm -rf rpms install: recurse_install uninstall: recurse_uninstall .PHONY: TAGS TAGS: etags -a libmultipath/*.c etags -a libmultipath/*.h etags -a multipathd/*.c etags -a multipathd/*.h release: sed -e "s/__VERSION__/${VERSION}/" \ multipath-tools.spec.in > multipath-tools.spec rpm: release rpmbuild -bb multipath-tools.spec multipath-tools-0.5.0+git1.656f8865/Makefile.inc000066400000000000000000000030441260771327300207620ustar00rootroot00000000000000# Makefile.inc # # Copyright (C) 2004 Christophe Varoqui, # # Allow to force some libraries to be used statically. (Uncomment one of the # following lines or define the values when calling make.) # # WITH_LOCAL_LIBDM = 1 # WITH_LOCAL_LIBSYSFS = 1 ifeq ($(TOPDIR),) TOPDIR = .. endif ifndef LIB ifeq ($(shell test -d /lib64 && echo 1),1) LIB=lib64 else LIB=lib endif endif ifndef SYSTEMD ifeq ($(shell systemctl --version > /dev/null 2>&1 && echo 1), 1) SYSTEMD = $(shell systemctl --version 2> /dev/null | sed -n 's/systemd \([0-9]*\)/\1/p') endif endif ifndef SYSTEMDPATH SYSTEMDPATH=usr/lib endif prefix = exec_prefix = $(prefix) bindir = $(exec_prefix)/sbin libudevdir = $(prefix)/$(SYSTEMDPATH)/udev udevrulesdir = $(libudevdir)/rules.d multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 man5dir = $(prefix)/usr/share/man/man5 man3dir = $(prefix)/usr/share/man/man3 rcdir = $(prefix)/etc/init.d syslibdir = $(prefix)/$(LIB) libdir = $(prefix)/$(LIB)/multipath unitdir = $(prefix)/$(SYSTEMDPATH)/systemd/system mpathpersistdir = $(TOPDIR)/libmpathpersist GZIP = gzip -9 -c INSTALL_PROGRAM = install ifndef RPM_OPT_FLAGS RPM_OPT_FLAGS = -O2 -g -pipe -Wformat-security -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 endif OPTFLAGS = $(RPM_OPT_FLAGS) -Wunused -Wstrict-prototypes CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\" SHARED_FLAGS = -shared %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< multipath-tools-0.5.0+git1.656f8865/README000066400000000000000000000000171260771327300174270ustar00rootroot00000000000000Things to read multipath-tools-0.5.0+git1.656f8865/TODO000066400000000000000000000000151260771327300172350ustar00rootroot00000000000000Things to do multipath-tools-0.5.0+git1.656f8865/getuid/000077500000000000000000000000001260771327300200325ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/getuid/usb_id000077500000000000000000000003251260771327300212250ustar00rootroot00000000000000#!/bin/sh # # copy in /bin and add this line to a device block in multipath.conf : # getuid_callout "/bin/usb_id %n" # cd /sys/block/$1 && cd $(ls -l device|cut -d">" -f2) && cd ../../../.. && cat serial multipath-tools-0.5.0+git1.656f8865/kpartx/000077500000000000000000000000001260771327300200625ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/kpartx/ChangeLog000066400000000000000000000004401260771327300216320ustar00rootroot00000000000000002: * convert to kpartx name everywhere * remove all HDGEO ioctl code * now work with files by mapping loops on the fly * merged and massage lopart.[ch] from lomount.[ch] (due credit to original author here : hpa ?) * added a fn find_loop_by_file in lopart.[ch] 001: * Initial release multipath-tools-0.5.0+git1.656f8865/kpartx/Makefile000066400000000000000000000023371260771327300215270ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc CFLAGS += -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 LIBDM_API_COOKIE = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_set_cookie' /usr/include/libdevmapper.h) ifneq ($(strip $(LIBDM_API_COOKIE)),0) CFLAGS += -DLIBDM_API_COOKIE endif LDFLAGS = -ldevmapper OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \ gpt.o mac.o ps3.o crc32.o lopart.o xstrncpy.o devmapper.o EXEC = kpartx all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(EXEC) $(EXEC).8 $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir) $(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir) $(INSTALL_PROGRAM) -d $(DESTDIR)/etc/udev/rules.d $(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/ $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz rm -f $(DESTDIR)$(libudevdir)/kpartx_id clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.5.0+git1.656f8865/kpartx/README000066400000000000000000000002531260771327300207420ustar00rootroot00000000000000This version of partx is intented to be build static against klibc. It creates partitions as device maps. With due respect to the original authors, have fun, cvaroqui multipath-tools-0.5.0+git1.656f8865/kpartx/bsd.c000066400000000000000000000073601260771327300210040ustar00rootroot00000000000000#include "kpartx.h" #include #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ #define XBSD_MAXPARTITIONS 16 #define BSD_FS_UNUSED 0 struct bsd_disklabel { unsigned int d_magic; /* the magic number */ short int d_type; /* drive type */ short int d_subtype; /* controller/d_type specific */ char d_typename[16]; /* type name, e.g. "eagle" */ char d_packname[16]; /* pack identifier */ unsigned int d_secsize; /* # of bytes per sector */ unsigned int d_nsectors; /* # of data sectors per track */ unsigned int d_ntracks; /* # of tracks per cylinder */ unsigned int d_ncylinders; /* # of data cylinders per unit */ unsigned int d_secpercyl; /* # of data sectors per cylinder */ unsigned int d_secperunit; /* # of data sectors per unit */ unsigned short d_sparespertrack;/* # of spare sectors per track */ unsigned short d_sparespercyl; /* # of spare sectors per cylinder */ unsigned int d_acylinders; /* # of alt. cylinders per unit */ unsigned short d_rpm; /* rotational speed */ unsigned short d_interleave; /* hardware sector interleave */ unsigned short d_trackskew; /* sector 0 skew, per track */ unsigned short d_cylskew; /* sector 0 skew, per cylinder */ unsigned int d_headswitch; /* head switch time, usec */ unsigned int d_trkseek; /* track-to-track seek, usec */ unsigned int d_flags; /* generic flags */ unsigned int d_drivedata[5]; /* drive-type specific information */ unsigned int d_spare[5]; /* reserved for future use */ unsigned int d_magic2; /* the magic number (again) */ unsigned short d_checksum; /* xor of data incl. partitions */ /* filesystem and partition information: */ unsigned short d_npartitions; /* number of partitions in following */ unsigned int d_bbsize; /* size of boot area at sn0, bytes */ unsigned int d_sbsize; /* max size of fs superblock, bytes */ struct bsd_partition { /* the partition table */ unsigned int p_size; /* number of sectors in partition */ unsigned int p_offset; /* starting sector */ unsigned int p_fsize; /* filesystem basic fragment size */ unsigned char p_fstype; /* filesystem type, see below */ unsigned char p_frag; /* filesystem fragments per block */ unsigned short p_cpg; /* filesystem cylinders per group */ } d_partitions[XBSD_MAXPARTITIONS];/* actually may be more */ }; int read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) { struct bsd_disklabel *l; struct bsd_partition *p; unsigned int offset = all.start, end; int max_partitions; char *bp; int n = 0, i, j; bp = getblock(fd, offset+1); /* 1 sector suffices */ if (bp == NULL) return -1; l = (struct bsd_disklabel *) bp; if (l->d_magic != BSD_DISKMAGIC) return -1; max_partitions = 16; if (l->d_npartitions < max_partitions) max_partitions = l->d_npartitions; for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { if (p->p_fstype == BSD_FS_UNUSED) /* nothing */; else if (n < ns) { sp[n].start = p->p_offset; sp[n].size = p->p_size; n++; } else { fprintf(stderr, "bsd_partition: too many slices\n"); break; } } /* * Convention has it that the bsd disklabel will always have * the 'c' partition spanning the entire disk. * So we have to check for contained slices. */ for(i = 0; i < n; i++) { if (sp[i].size == 0) continue; end = sp[i].start + sp[i].size; for(j = 0; j < n; j ++) { if ( i == j ) continue; if (sp[j].size == 0) continue; if (sp[i].start < sp[j].start) { if (end > sp[j].start && end < sp[j].start + sp[j].size) { /* Invalid slice */ fprintf(stderr, "bsd_disklabel: slice %d overlaps with %d\n", i , j); sp[i].size = 0; } } else { if (end <= sp[j].start + sp[j].size) { sp[i].container = j + 1; } } } } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/byteorder.h000066400000000000000000000012601260771327300222310ustar00rootroot00000000000000#ifndef BYTEORDER_H_INCLUDED #define BYTEORDER_H_INCLUDED #ifdef __linux__ # include # include #else # error unsupported #endif #if BYTE_ORDER == LITTLE_ENDIAN # define le16_to_cpu(x) (x) # define be16_to_cpu(x) bswap_16(x) # define le32_to_cpu(x) (x) # define le64_to_cpu(x) (x) # define be32_to_cpu(x) bswap_32(x) # define be64_to_cpu(x) bswap_64(x) #elif BYTE_ORDER == BIG_ENDIAN # define le16_to_cpu(x) bswap_16(x) # define be16_to_cpu(x) (x) # define le32_to_cpu(x) bswap_32(x) # define le64_to_cpu(x) bswap_64(x) # define be32_to_cpu(x) (x) # define be64_to_cpu(x) (x) #else # error unsupported #endif #endif /* BYTEORDER_H_INCLUDED */ multipath-tools-0.5.0+git1.656f8865/kpartx/crc32.c000066400000000000000000000315351260771327300211510ustar00rootroot00000000000000/* * crc32.c * This code is in the public domain; copyright abandoned. * Liability for non-performance of this code is limited to the amount * you paid for it. Since it is distributed for free, your refund will * be very very small. If it breaks, you get to keep both pieces. */ #include "crc32.h" #if __GNUC__ >= 3 /* 2.x has "attribute", but only 3.0 has "pure */ #define attribute(x) __attribute__(x) #else #define attribute(x) #endif /* * There are multiple 16-bit CRC polynomials in common use, but this is * *the* standard CRC-32 polynomial, first popularized by Ethernet. * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */ #define CRCPOLY_LE 0xedb88320 #define CRCPOLY_BE 0x04c11db7 /* How many bits at a time to use. Requires a table of 4< 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 # error CRC_LE_BITS must be a power of 2 between 1 and 8 #endif #if CRC_LE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ #define crc32init_le() #define crc32cleanup_le() /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len) { int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); } return crc; } #else /* Table-based approach */ static uint32_t *crc32table_le; /** * crc32init_le() - allocate and initialize LE table data * * crc is the crc of the byte i; other entries are filled in based on the * fact that crctable[i^j] = crctable[i] ^ crctable[j]. * */ static int crc32init_le(void) { unsigned i, j; uint32_t crc = 1; crc32table_le = malloc((1 << CRC_LE_BITS) * sizeof(uint32_t)); if (!crc32table_le) return 1; crc32table_le[0] = 0; for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) { crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i) crc32table_le[i + j] = crc ^ crc32table_le[j]; } return 0; } /** * crc32cleanup_le(): free LE table data */ static void crc32cleanup_le(void) { if (crc32table_le) free(crc32table_le); crc32table_le = NULL; } /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len) { while (len--) { # if CRC_LE_BITS == 8 crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255]; # elif CRC_LE_BITS == 4 crc ^= *p++; crc = (crc >> 4) ^ crc32table_le[crc & 15]; crc = (crc >> 4) ^ crc32table_le[crc & 15]; # elif CRC_LE_BITS == 2 crc ^= *p++; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; # endif } return crc; } #endif /* * Big-endian CRC computation. Used with serial bit streams sent * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. */ #if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 # error CRC_BE_BITS must be a power of 2 between 1 and 8 #endif #if CRC_BE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ #define crc32init_be() #define crc32cleanup_be() /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len) { int i; while (len--) { crc ^= *p++ << 24; for (i = 0; i < 8; i++) crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); } return crc; } #else /* Table-based approach */ static uint32_t *crc32table_be; /** * crc32init_be() - allocate and initialize BE table data */ static int crc32init_be(void) { unsigned i, j; uint32_t crc = 0x80000000; crc32table_be = malloc((1 << CRC_BE_BITS) * sizeof(uint32_t)); if (!crc32table_be) return 1; crc32table_be[0] = 0; for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) { crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); for (j = 0; j < i; j++) crc32table_be[i + j] = crc ^ crc32table_be[j]; } return 0; } /** * crc32cleanup_be(): free BE table data */ static void crc32cleanup_be(void) { if (crc32table_be) free(crc32table_be); crc32table_be = NULL; } /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len) { while (len--) { # if CRC_BE_BITS == 8 crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++]; # elif CRC_BE_BITS == 4 crc ^= *p++ << 24; crc = (crc << 4) ^ crc32table_be[crc >> 28]; crc = (crc << 4) ^ crc32table_be[crc >> 28]; # elif CRC_BE_BITS == 2 crc ^= *p++ << 24; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; # endif } return crc; } #endif /* * A brief CRC tutorial. * * A CRC is a long-division remainder. You add the CRC to the message, * and the whole thing (message+CRC) is a multiple of the given * CRC polynomial. To check the CRC, you can either check that the * CRC matches the recomputed value, *or* you can check that the * remainder computed on the message+CRC is 0. This latter approach * is used by a lot of hardware implementations, and is why so many * protocols put the end-of-frame flag after the CRC. * * It's actually the same long division you learned in school, except that * - We're working in binary, so the digits are only 0 and 1, and * - When dividing polynomials, there are no carries. Rather than add and * subtract, we just xor. Thus, we tend to get a bit sloppy about * the difference between adding and subtracting. * * A 32-bit CRC polynomial is actually 33 bits long. But since it's * 33 bits long, bit 32 is always going to be set, so usually the CRC * is written in hex with the most significant bit omitted. (If you're * familiar with the IEEE 754 floating-point format, it's the same idea.) * * Note that a CRC is computed over a string of *bits*, so you have * to decide on the endianness of the bits within each byte. To get * the best error-detecting properties, this should correspond to the * order they're actually sent. For example, standard RS-232 serial is * little-endian; the most significant bit (sometimes used for parity) * is sent last. And when appending a CRC word to a message, you should * do it in the right order, matching the endianness. * * Just like with ordinary division, the remainder is always smaller than * the divisor (the CRC polynomial) you're dividing by. Each step of the * division, you take one more digit (bit) of the dividend and append it * to the current remainder. Then you figure out the appropriate multiple * of the divisor to subtract to being the remainder back into range. * In binary, it's easy - it has to be either 0 or 1, and to make the * XOR cancel, it's just a copy of bit 32 of the remainder. * * When computing a CRC, we don't care about the quotient, so we can * throw the quotient bit away, but subtract the appropriate multiple of * the polynomial from the remainder and we're back to where we started, * ready to process the next bit. * * A big-endian CRC written this way would be coded like: * for (i = 0; i < input_bits; i++) { * multiple = remainder & 0x80000000 ? CRCPOLY : 0; * remainder = (remainder << 1 | next_input_bit()) ^ multiple; * } * Notice how, to get at bit 32 of the shifted remainder, we look * at bit 31 of the remainder *before* shifting it. * * But also notice how the next_input_bit() bits we're shifting into * the remainder don't actually affect any decision-making until * 32 bits later. Thus, the first 32 cycles of this are pretty boring. * Also, to add the CRC to a message, we need a 32-bit-long hole for it at * the end, so we have to add 32 extra cycles shifting in zeros at the * end of every message, * * So the standard trick is to rearrage merging in the next_input_bit() * until the moment it's needed. Then the first 32 cycles can be precomputed, * and merging in the final 32 zero bits to make room for the CRC can be * skipped entirely. * This changes the code to: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit() << 31; * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * With this optimization, the little-endian code is simpler: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit(); * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder >> 1) ^ multiple; * } * * Note that the other details of endianness have been hidden in CRCPOLY * (which must be bit-reversed) and next_input_bit(). * * However, as long as next_input_bit is returning the bits in a sensible * order, we can actually do the merging 8 or more bits at a time rather * than one bit at a time: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte() << 24; * for (j = 0; j < 8; j++) { * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * Or in little-endian: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte(); * for (j = 0; j < 8; j++) { * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * If the input is a multiple of 32 bits, you can even XOR in a 32-bit * word at a time and increase the inner loop count to 32. * * You can also mix and match the two loop styles, for example doing the * bulk of a message byte-at-a-time and adding bit-at-a-time processing * for any fractional bytes at the end. * * The only remaining optimization is to the byte-at-a-time table method. * Here, rather than just shifting one bit of the remainder to decide * in the correct multiple to subtract, we can shift a byte at a time. * This produces a 40-bit (rather than a 33-bit) intermediate remainder, * but again the multiple of the polynomial to subtract depends only on * the high bits, the high 8 bits in this case. * * The multile we need in that case is the low 32 bits of a 40-bit * value whose high 8 bits are given, and which is a multiple of the * generator polynomial. This is simply the CRC-32 of the given * one-byte message. * * Two more details: normally, appending zero bits to a message which * is already a multiple of a polynomial produces a larger multiple of that * polynomial. To enable a CRC to detect this condition, it's common to * invert the CRC before appending it. This makes the remainder of the * message+crc come out not as zero, but some fixed non-zero value. * * The same problem applies to zero bits prepended to the message, and * a similar solution is used. Instead of starting with a remainder of * 0, an initial remainder of all ones is used. As long as you start * the same way on decoding, it doesn't make a difference. */ /** * init_crc32(): generates CRC32 tables * * On successful initialization, use count is increased. * This guarantees that the library functions will stay resident * in memory, and prevents someone from 'rmmod crc32' while * a driver that needs it is still loaded. * This also greatly simplifies drivers, as there's no need * to call an initialization/cleanup function from each driver. * Since crc32.o is a library module, there's no requirement * that the user can unload it. */ int init_crc32(void) { int rc1, rc2, rc; rc1 = crc32init_le(); rc2 = crc32init_be(); rc = rc1 || rc2; return rc; } /** * cleanup_crc32(): frees crc32 data when no longer needed */ void cleanup_crc32(void) { crc32cleanup_le(); crc32cleanup_be(); } multipath-tools-0.5.0+git1.656f8865/kpartx/crc32.h000066400000000000000000000010431260771327300211450ustar00rootroot00000000000000/* * crc32.h */ #ifndef _CRC32_H #define _CRC32_H #include #include extern int init_crc32(void); extern void cleanup_crc32(void); extern uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len); extern uint32_t crc32_be(uint32_t crc, unsigned char const *p, size_t len); #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)data, length) #define ether_crc_le(length, data) crc32_le(~0, data, length) #define ether_crc(length, data) crc32_be(~0, data, length) #endif /* _CRC32_H */ multipath-tools-0.5.0+git1.656f8865/kpartx/dasd.c000066400000000000000000000144471260771327300211530ustar00rootroot00000000000000/* * dasd.c * * IBM DASD partition table handling. * * Mostly taken from drivers/s390/block/dasd.c * * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH * Copyright IBM Corporation, 2009 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "devmapper.h" #include "kpartx.h" #include "byteorder.h" #include "dasd.h" unsigned long long sectors512(unsigned long long sectors, int blocksize) { return sectors * (blocksize >> 9); } typedef unsigned int __attribute__((__may_alias__)) label_ints_t; /* */ int read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) { int retval = -1; int blocksize; uint64_t disksize; uint64_t offset, size, fmt_size; dasd_information_t info; struct hd_geometry geo; char type[5] = {0,}; volume_label_t vlabel; unsigned char *data = NULL; uint64_t blk; int fd_dasd = -1; struct stat sbuf; dev_t dev; char *devname; char pathname[256]; if (fd < 0) { return -1; } if (fstat(fd, &sbuf) == -1) { return -1; } devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev)); if (devname != NULL) { /* We were passed a handle to a dm device. * Get the first target and operate on that instead. */ if (!(dev = dm_get_first_dep(devname))) { free(devname); return -1; } free(devname); if ((unsigned int)major(dev) != 94) { /* Not a DASD */ return -1; } /* * Hard to believe, but there's no simple way to translate * major/minor into an openable device file, so we have * to create one for ourselves. */ sprintf(pathname, "/dev/.kpartx-node-%u-%u", (unsigned int)major(dev), (unsigned int)minor(dev)); if ((fd_dasd = open(pathname, O_RDONLY)) == -1) { /* Devicenode does not exist. Try to create one */ if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) { /* Couldn't create a device node */ return -1; } fd_dasd = open(pathname, O_RDONLY); /* * The file will vanish when the last process (we) * has ceased to access it. */ unlink(pathname); } if (!fd_dasd) { /* Couldn't open the device */ return -1; } } else { fd_dasd = fd; } if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) { goto out; } if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) { goto out; } if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0) goto out; disksize >>= 9; if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0) goto out; if (blocksize < 512 || blocksize > 4096) goto out; /* * Get volume label, extract name and type. */ if (!(data = (unsigned char *)malloc(blocksize))) goto out; if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1) goto out; if (read(fd_dasd, data, blocksize) == -1) { perror("read"); goto out; } if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) memcpy (&vlabel, data, sizeof(vlabel)); else { bzero(&vlabel,4); memcpy (&vlabel.vollbl, data, sizeof(vlabel) - 4); } vtoc_ebcdic_dec(vlabel.vollbl, type, 4); /* * Three different types: CMS1, VOL1 and LNX1/unlabeled */ if (strncmp(type, "CMS1", 4) == 0) { /* * VM style CMS1 labeled disk */ label_ints_t *label = (label_ints_t *) &vlabel; blocksize = label[4]; if (label[14] != 0) { /* disk is reserved minidisk */ offset = label[14]; size = sectors512(label[8] - 1, blocksize); } else { offset = info.label_block + 1; size = sectors512(label[8], blocksize); } sp[0].start = sectors512(offset, blocksize); sp[0].size = size - sp[0].start; retval = 1; } else if ((strncmp(type, "VOL1", 4) == 0) && (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) { /* * New style VOL1 labeled disk */ int counter; /* get block number and read then go through format1 labels */ blk = cchhb2blk(&vlabel.vtoc, &geo) + 1; counter = 0; if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1) goto out; while (read(fd_dasd, data, blocksize) != -1) { format1_label_t f1; memcpy(&f1, data, sizeof(format1_label_t)); /* skip FMT4 / FMT5 / FMT7 labels */ if (EBCtoASC[f1.DS1FMTID] == '4' || EBCtoASC[f1.DS1FMTID] == '5' || EBCtoASC[f1.DS1FMTID] == '7' || EBCtoASC[f1.DS1FMTID] == '9') { blk++; continue; } /* only FMT1 and FMT8 valid at this point */ if (EBCtoASC[f1.DS1FMTID] != '1' && EBCtoASC[f1.DS1FMTID] != '8') break; /* OK, we got valid partition data */ offset = cchh2blk(&f1.DS1EXT1.llimit, &geo); size = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - offset + geo.sectors; sp[counter].start = sectors512(offset, blocksize); sp[counter].size = sectors512(size, blocksize); counter++; blk++; } retval = counter; } else { /* * Old style LNX1 or unlabeled disk */ if (strncmp(type, "LNX1", 4) == 0) { if (vlabel.ldl_version == 0xf2) { fmt_size = sectors512(vlabel.formatted_blocks, blocksize); } else if (!strcmp(info.type, "ECKD")) { /* formated w/o large volume support */ fmt_size = geo.cylinders * geo.heads * geo.sectors * (blocksize >> 9); } else { /* old label and no usable disk geometry * (e.g. DIAG) */ fmt_size = disksize; } size = disksize; if (fmt_size < size) size = fmt_size; } else size = disksize; sp[0].start = sectors512(info.label_block + 1, blocksize); sp[0].size = size - sp[0].start; retval = 1; } out: if (data != NULL) free(data); if (fd_dasd != -1 && fd_dasd != fd) close(fd_dasd); return retval; } multipath-tools-0.5.0+git1.656f8865/kpartx/dasd.h000066400000000000000000000302361260771327300211520ustar00rootroot00000000000000/* * dasd.h * * IBM DASD partition table handling. * * Mostly taken from drivers/s390/block/dasd.c * * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH * Copyright IBM Corporation, 2009 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifndef _DASD_H #define _DASD_H typedef struct ttr { uint16_t tt; uint8_t r; } __attribute__ ((packed)) ttr_t; typedef struct cchhb { uint16_t cc; uint16_t hh; uint8_t b; } __attribute__ ((packed)) cchhb_t; typedef struct cchh { uint16_t cc; uint16_t hh; } __attribute__ ((packed)) cchh_t; typedef struct labeldate { uint8_t year; uint16_t day; } __attribute__ ((packed)) labeldate_t; typedef struct volume_label { char volkey[4]; /* volume key = volume label */ char vollbl[4]; /* volume label */ char volid[6]; /* volume identifier */ uint8_t security; /* security byte */ cchhb_t vtoc; /* VTOC address */ char res1[5]; /* reserved */ char cisize[4]; /* CI-size for FBA,... */ /* ...blanks for CKD */ char blkperci[4]; /* no of blocks per CI (FBA), blanks for CKD */ char labperci[4]; /* no of labels per CI (FBA), blanks for CKD */ char res2[4]; /* reserved */ char lvtoc[14]; /* owner code for LVTOC */ char res3[28]; /* reserved */ uint8_t ldl_version; /* version number, valid for ldl format */ uint64_t formatted_blocks; /* valid when ldl_version >= f2 */ } __attribute__ ((packed)) volume_label_t; typedef struct extent { uint8_t typeind; /* extent type indicator */ uint8_t seqno; /* extent sequence number */ cchh_t llimit; /* starting point of this extent */ cchh_t ulimit; /* ending point of this extent */ } __attribute__ ((packed)) extent_t; typedef struct dev_const { uint16_t DS4DSCYL; /* number of logical cyls */ uint16_t DS4DSTRK; /* number of tracks in a logical cylinder */ uint16_t DS4DEVTK; /* device track length */ uint8_t DS4DEVI; /* non-last keyed record overhead */ uint8_t DS4DEVL; /* last keyed record overhead */ uint8_t DS4DEVK; /* non-keyed record overhead differential */ uint8_t DS4DEVFG; /* flag byte */ uint16_t DS4DEVTL; /* device tolerance */ uint8_t DS4DEVDT; /* number of DSCB's per track */ uint8_t DS4DEVDB; /* number of directory blocks per track */ } __attribute__ ((packed)) dev_const_t; typedef struct format1_label { char DS1DSNAM[44]; /* data set name */ uint8_t DS1FMTID; /* format identifier */ char DS1DSSN[6]; /* data set serial number */ uint16_t DS1VOLSQ; /* volume sequence number */ labeldate_t DS1CREDT; /* creation date: ydd */ labeldate_t DS1EXPDT; /* expiration date */ uint8_t DS1NOEPV; /* number of extents on volume */ uint8_t DS1NOBDB; /* no. of bytes used in last direction blk */ uint8_t DS1FLAG1; /* flag 1 */ char DS1SYSCD[13]; /* system code */ labeldate_t DS1REFD; /* date last referenced */ uint8_t DS1SMSFG; /* system managed storage indicators */ uint8_t DS1SCXTF; /* sec. space extension flag byte */ uint16_t DS1SCXTV; /* secondary space extension value */ uint8_t DS1DSRG1; /* data set organisation byte 1 */ uint8_t DS1DSRG2; /* data set organisation byte 2 */ uint8_t DS1RECFM; /* record format */ uint8_t DS1OPTCD; /* option code */ uint16_t DS1BLKL; /* block length */ uint16_t DS1LRECL; /* record length */ uint8_t DS1KEYL; /* key length */ uint16_t DS1RKP; /* relative key position */ uint8_t DS1DSIND; /* data set indicators */ uint8_t DS1SCAL1; /* secondary allocation flag byte */ char DS1SCAL3[3]; /* secondary allocation quantity */ ttr_t DS1LSTAR; /* last used track and block on track */ uint16_t DS1TRBAL; /* space remaining on last used track */ uint16_t res1; /* reserved */ extent_t DS1EXT1; /* first extent description */ extent_t DS1EXT2; /* second extent description */ extent_t DS1EXT3; /* third extent description */ cchhb_t DS1PTRDS; /* possible pointer to f2 or f3 DSCB */ } __attribute__ ((packed)) format1_label_t; /* * struct dasd_information_t * represents any data about the data, which is visible to userspace */ typedef struct dasd_information_t { unsigned int devno; /* S/390 devno */ unsigned int real_devno; /* for aliases */ unsigned int schid; /* S/390 subchannel identifier */ unsigned int cu_type : 16; /* from SenseID */ unsigned int cu_model : 8; /* from SenseID */ unsigned int dev_type : 16; /* from SenseID */ unsigned int dev_model : 8; /* from SenseID */ unsigned int open_count; unsigned int req_queue_len; unsigned int chanq_len; /* length of chanq */ char type[4]; /* from discipline.name, 'none' for unknown */ unsigned int status; /* current device level */ unsigned int label_block; /* where to find the VOLSER */ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ unsigned int characteristics_size; unsigned int confdata_size; char characteristics[64]; /* from read_device_characteristics */ char configuration_data[256]; /* from read_configuration_data */ } dasd_information_t; #define DASD_IOCTL_LETTER 'D' #define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t) #define BLKGETSIZE _IO(0x12,96) #define BLKSSZGET _IO(0x12,104) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* device size in bytes (u64 *arg)*/ /* * Only compile this on S/390. Doesn't make any sense * for other architectures. */ static unsigned char EBCtoASC[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -ENP ->LF */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -IUS */ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -INP */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -SW */ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 SP RSP ä ---- */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, /* 0x48 . < ( + | */ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 & ---- */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, /* 0x58 ß ! $ * ) ; */ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, /* 0x60 - / ---- Ä ---- ---- ---- */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, /* 0x68 ---- , % _ > ? */ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 --- ---- ---- ---- ---- ---- ---- */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x78 * ` : # @ ' = " */ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 * a b c d e f g */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x88 h i ---- ---- ---- */ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 ° j k l m n o p */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, /* 0x98 q r ---- ---- */ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xA0 ~ s t u v w x */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* 0xA8 y z ---- ---- ---- ---- */ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, /* 0xB0 ^ ---- § ---- */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, /* 0xB8 ---- [ ] ---- ---- ---- ---- */ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, /* 0xC0 { A B C D E F G */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xC8 H I ---- ö ---- */ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xD0 } J K L M N O P */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, /* 0xD8 Q R ---- ü */ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xE0 \ S T U V W X */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* 0xE8 Y Z ---- Ö ---- ---- ---- */ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xF0 0 1 2 3 4 5 6 7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; static inline void vtoc_ebcdic_dec (const char *source, char *target, int l) { int i; for (i = 0; i < l; i++) target[i]=(char)EBCtoASC[(unsigned char)(source[i])]; } /* * compute the block number from a * cyl-cyl-head-head structure */ static inline uint64_t cchh2blk (cchh_t *ptr, struct hd_geometry *geo) { uint64_t cyl; uint16_t head; /*decode cylinder and heads for large volumes */ cyl = ptr->hh & 0xFFF0; cyl <<= 12; cyl |= ptr->cc; head = ptr->hh & 0x000F; return cyl * geo->heads * geo->sectors + head * geo->sectors; } /* * compute the block number from a * cyl-cyl-head-head-block structure */ static inline uint64_t cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) { uint64_t cyl; uint16_t head; /*decode cylinder and heads for large volumes */ cyl = ptr->hh & 0xFFF0; cyl <<= 12; cyl |= ptr->cc; head = ptr->hh & 0x000F; return cyl * geo->heads * geo->sectors + head * geo->sectors + ptr->b; } #endif /* _DASD_H */ multipath-tools-0.5.0+git1.656f8865/kpartx/devmapper.c000066400000000000000000000153051260771327300222150ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include "devmapper.h" #define UUID_PREFIX "part%d-" #define MAX_PREFIX_LEN 8 #define PARAMS_SIZE 1024 extern int dm_prereq (char * str, int x, int y, int z) { int r = 1; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; target = dm_task_get_versions(dmt); /* Fetch targets and print 'em */ do { last_target = target; if (!strncmp(str, target->name, strlen(str)) && /* dummy prereq on multipath version */ target->version[0] >= x && target->version[1] >= y && target->version[2] >= z ) r = 0; target = (void *) target + target->next; } while (last_target != target); out: dm_task_destroy(dmt); return r; } extern int dm_simplecmd (int task, const char *name, int no_flush, uint16_t udev_flags) { int r = 0; int udev_wait_flag = (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE); #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; #endif struct dm_task *dmt; if (!(dmt = dm_task_create(task))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); if (no_flush) dm_task_no_flush(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) { dm_udev_complete(cookie); goto out; } #endif r = dm_task_run(dmt); #ifdef LIBDM_API_COOKIE if (udev_wait_flag) { if (!r) dm_udev_complete(cookie); else dm_udev_wait(cookie); } #endif out: dm_task_destroy(dmt); return r; } extern int dm_addmap (int task, const char *name, const char *target, const char *params, uint64_t size, int ro, const char *uuid, int part, mode_t mode, uid_t uid, gid_t gid) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; uint16_t udev_flags = 0; #endif if (!(dmt = dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, name)) goto addout; if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; if (ro && !dm_task_set_ro (dmt)) goto addout; if (task == DM_DEVICE_CREATE && uuid) { prefixed_uuid = malloc(MAX_PREFIX_LEN + strlen(uuid) + 1); if (!prefixed_uuid) { fprintf(stderr, "cannot create prefixed uuid : %s\n", strerror(errno)); goto addout; } sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid); if (!dm_task_set_uuid(dmt, prefixed_uuid)) goto addout; } if (!dm_task_set_mode(dmt, mode)) goto addout; if (!dm_task_set_uid(dmt, uid)) goto addout; if (!dm_task_set_gid(dmt, gid)) goto addout; dm_task_no_open_count(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &cookie, udev_flags)) { dm_udev_complete(cookie); goto addout; } #endif r = dm_task_run (dmt); #ifdef LIBDM_API_COOKIE if (task == DM_DEVICE_CREATE) { if (!r) dm_udev_complete(cookie); else dm_udev_wait(cookie); } #endif addout: dm_task_destroy (dmt); free(prefixed_uuid); return r; } extern int dm_map_present (char * str) { int r = 0; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, str)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (info.exists) r = 1; out: dm_task_destroy(dmt); return r; } char * dm_mapname(int major, int minor) { struct dm_task *dmt; char *mapname = NULL; const char *map; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; dm_task_no_open_count(dmt); dm_task_set_major(dmt, major); dm_task_set_minor(dmt, minor); if (!dm_task_run(dmt)) goto out; map = dm_task_get_name(dmt); if (map && strlen(map)) mapname = strdup(map); out: dm_task_destroy(dmt); return mapname; } /* * dm_get_first_dep * * Return the device number of the first dependend device * for a given target. */ dev_t dm_get_first_dep(char *devname) { struct dm_task *dmt; struct dm_deps *dm_deps; dev_t ret = 0; if ((dmt = dm_task_create(DM_DEVICE_DEPS)) == NULL) { return ret; } if (!dm_task_set_name(dmt, devname)) { goto out; } if (!dm_task_run(dmt)) { goto out; } if ((dm_deps = dm_task_get_deps(dmt)) == NULL) { goto out; } if (dm_deps->count > 0) { ret = dm_deps->device[0]; } out: dm_task_destroy(dmt); return ret; } char * dm_mapuuid(int major, int minor) { struct dm_task *dmt; const char *tmp; char *uuid = NULL; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; dm_task_no_open_count(dmt); dm_task_set_major(dmt, major); dm_task_set_minor(dmt, minor); if (!dm_task_run(dmt)) goto out; tmp = dm_task_get_uuid(dmt); if (tmp[0] != '\0') uuid = strdup(tmp); out: dm_task_destroy(dmt); return uuid; } int dm_devn (char * mapname, int *major, int *minor) { int r = 1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; *major = info.major; *minor = info.minor; r = 0; out: dm_task_destroy(dmt); return r; } int dm_get_map(int major, int minor, char * outparams) { int r = 1; struct dm_task *dmt; void *next = NULL; uint64_t start, length; char *target_type = NULL; char *params = NULL; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 1; dm_task_set_major(dmt, major); dm_task_set_minor(dmt, minor); dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } #define FEATURE_NO_PART "no_partitions" int dm_no_partitions(int major, int minor) { char params[PARAMS_SIZE], *ptr; int i, num_features; if (dm_get_map(major, minor, params)) return 0; ptr = params; num_features = strtoul(params, &ptr, 10); if ((ptr == params) || num_features == 0) { /* No features found, return success */ return 0; } for (i = 0; (i < num_features); i++) { if (!ptr || ptr > params + strlen(params)) break; /* Skip whitespaces */ while(ptr && *ptr == ' ') ptr++; if (!strncmp(ptr, FEATURE_NO_PART, strlen(FEATURE_NO_PART))) return 1; ptr = strchr(ptr, ' '); } return 0; } multipath-tools-0.5.0+git1.656f8865/kpartx/devmapper.h000066400000000000000000000013121260771327300222130ustar00rootroot00000000000000#ifndef _KPARTX_DEVMAPPER_H #define _KPARTX_DEVMAPPER_H #ifdef DM_SUBSYSTEM_UDEV_FLAG0 #define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 #else #define MPATH_UDEV_RELOAD_FLAG 0 #endif extern int udev_sync; int dm_prereq (char *, int, int, int); int dm_simplecmd (int, const char *, int, uint16_t); int dm_addmap (int, const char *, const char *, const char *, uint64_t, int, const char *, int, mode_t, uid_t, gid_t); int dm_map_present (char *); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); char * dm_mapuuid(int major, int minor); int dm_devn (char * mapname, int *major, int *minor); int dm_no_partitions(int major, int minor); #endif /* _KPARTX_DEVMAPPER_H */ multipath-tools-0.5.0+git1.656f8865/kpartx/dos.c000066400000000000000000000047451260771327300210250ustar00rootroot00000000000000/* * Source: copy of util-linux' partx dos.c * * Copyrights of the original file apply * Copyright (c) 2005 Bastian Blank */ #include "kpartx.h" #include "byteorder.h" #include #include #include "dos.h" static int is_extended(int type) { return (type == 5 || type == 0xf || type == 0x85); } static int read_extended_partition(int fd, struct partition *ep, int en, struct slice *sp, int ns) { struct partition p; unsigned long start, here, next; unsigned char *bp; int loopct = 0; int moretodo = 1; int i, n=0; int sector_size_mul = get_sector_size(fd)/512; next = start = sector_size_mul * le32_to_cpu(ep->start_sect); while (moretodo) { here = next; moretodo = 0; if (++loopct > 100) return n; bp = (unsigned char *)getblock(fd, here); if (bp == NULL) return n; if (bp[510] != 0x55 || bp[511] != 0xaa) return n; for (i=0; i<2; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_extended(p.sys_type)) { if (p.nr_sects && !moretodo) { next = start + sector_size_mul * le32_to_cpu(p.start_sect); moretodo = 1; } continue; } if (n < ns) { sp[n].start = here + sector_size_mul * le32_to_cpu(p.start_sect); sp[n].size = sector_size_mul * le32_to_cpu(p.nr_sects); sp[n].container = en + 1; n++; } else { fprintf(stderr, "dos_extd_partition: too many slices\n"); return n; } loopct = 0; } } return n; } static int is_gpt(int type) { return (type == 0xEE); } int read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) { struct partition p; unsigned long offset = all.start; int i, n=4; unsigned char *bp; int sector_size_mul = get_sector_size(fd)/512; bp = (unsigned char *)getblock(fd, offset); if (bp == NULL) return -1; if (bp[510] != 0x55 || bp[511] != 0xaa) return -1; for (i=0; i<4; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_gpt(p.sys_type)) return 0; if (i < ns) { sp[i].start = sector_size_mul * le32_to_cpu(p.start_sect); sp[i].size = sector_size_mul * le32_to_cpu(p.nr_sects); } else { fprintf(stderr, "dos_partition: too many slices\n"); break; } if (is_extended(p.sys_type)) { /* extended partitions only get one or two sectors mapped for LILO to install, whichever is needed to have 1kb of space */ if (sector_size_mul == 1) sp[i].size = 2; else sp[i].size = sector_size_mul; n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/dos.h000066400000000000000000000004531260771327300210220ustar00rootroot00000000000000#ifndef DOS_H_INCLUDED #define DOS_H_INCLUDED struct partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char bh, bs, bc; unsigned char sys_type; unsigned char eh, es, ec; unsigned int start_sect; unsigned int nr_sects; } __attribute__((packed)); #endif /* DOS_H_INCLUDED */ multipath-tools-0.5.0+git1.656f8865/kpartx/efi.h000066400000000000000000000034361260771327300210040ustar00rootroot00000000000000/* efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars Copyright (C) 2001 Dell Computer Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef EFI_H #define EFI_H /* * Extensible Firmware Interface * Based on 'Extensible Firmware Interface Specification' * version 1.02, 12 December, 2000 */ #include #include typedef struct { uint8_t b[16]; } efi_guid_t; #define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ ((efi_guid_t) \ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ (b) & 0xff, ((b) >> 8) & 0xff, \ (c) & 0xff, ((c) >> 8) & 0xff, \ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) /****************************************************** * GUIDs ******************************************************/ #define NULL_GUID \ EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) static inline int efi_guidcmp(efi_guid_t left, efi_guid_t right) { return memcmp(&left, &right, sizeof (efi_guid_t)); } typedef uint16_t efi_char16_t; /* UNICODE character */ #endif /* EFI_H */ multipath-tools-0.5.0+git1.656f8865/kpartx/gpt.c000066400000000000000000000455421260771327300210320ustar00rootroot00000000000000/* gpt.[ch] Copyright (C) 2000-2001 Dell Computer Corporation EFI GUID Partition Table handling Per Intel EFI Specification v1.02 http://developer.intel.com/technology/efi/efi.htm This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _FILE_OFFSET_BITS 64 #include "gpt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "crc32.h" #include "kpartx.h" #if BYTE_ORDER == LITTLE_ENDIAN # define __le16_to_cpu(x) (x) # define __le32_to_cpu(x) (x) # define __le64_to_cpu(x) (x) # define __cpu_to_le32(x) (x) #elif BYTE_ORDER == BIG_ENDIAN # define __le16_to_cpu(x) bswap_16(x) # define __le32_to_cpu(x) bswap_32(x) # define __le64_to_cpu(x) bswap_64(x) # define __cpu_to_le32(x) bswap_32(x) #endif #ifndef BLKGETLASTSECT #define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ #endif #ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #ifndef BLKSSZGET #define BLKSSZGET _IO(0x12,104) /* get block device sector size */ #endif #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ #endif struct blkdev_ioctl_param { unsigned int block; size_t content_length; char * block_contents; }; /** * efi_crc32() - EFI version of crc32 function * @buf: buffer to calculate crc32 of * @len - length of buf * * Description: Returns EFI-style CRC32 value for @buf * * This function uses the little endian Ethernet polynomial * but seeds the function with ~0, and xor's with ~0 at the end. * Note, the EFI Specification, v1.02, has a reference to * Dr. Dobbs Journal, May 1994 (actually it's in May 1992). */ static inline uint32_t efi_crc32(const void *buf, unsigned long len) { return (crc32(~0L, buf, len) ^ ~0L); } /** * is_pmbr_valid(): test Protective MBR for validity * @mbr: pointer to a legacy mbr structure * * Description: Returns 1 if PMBR is valid, 0 otherwise. * Validity depends on two things: * 1) MSDOS signature is in the last two bytes of the MBR * 2) One partition of type 0xEE is found */ static int is_pmbr_valid(legacy_mbr *mbr) { int i, found = 0, signature = 0; if (!mbr) return 0; signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE); for (i = 0; signature && i < 4; i++) { if (mbr->partition[i].sys_type == EFI_PMBR_OSTYPE_EFI_GPT) { found = 1; break; } } return (signature && found); } /************************************************************ * _get_num_sectors * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * Last LBA value on success * 0 on error * * Try getting BLKGETSIZE64 and BLKSSZGET first, * then BLKGETSIZE if necessary. * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 * which returns the number of 512-byte sectors, not the size of * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. ************************************************************/ static uint64_t _get_num_sectors(int filedes) { int rc; uint64_t bytes=0; rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); return 0; } /************************************************************ * last_lba(): return number of last logical block of device * * @fd * * Description: returns Last LBA value on success, 0 on error. * Notes: The value st_blocks gives the size of the file * in 512-byte blocks, which is OK if * EFI_BLOCK_SIZE_SHIFT == 9. ************************************************************/ static uint64_t last_lba(int filedes) { int rc; uint64_t sectors = 0; struct stat s; memset(&s, 0, sizeof (s)); rc = fstat(filedes, &s); if (rc == -1) { fprintf(stderr, "last_lba() could not stat: %s\n", strerror(errno)); return 0; } if (S_ISBLK(s.st_mode)) { sectors = _get_num_sectors(filedes); } else { fprintf(stderr, "last_lba(): I don't know how to handle files with mode %x\n", s.st_mode); sectors = 1; } return sectors ? sectors - 1 : 0; } static ssize_t read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count) { int rc; struct blkdev_ioctl_param ioctl_param; if (!buffer) return 0; ioctl_param.block = 0; /* read the last sector */ ioctl_param.content_length = count; ioctl_param.block_contents = buffer; rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param); if (rc == -1) perror("read failed"); return !rc; } static ssize_t read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) { int sector_size = get_sector_size(fd); off_t offset = lba * sector_size; uint64_t lastlba; ssize_t bytesread; lseek(fd, offset, SEEK_SET); bytesread = read(fd, buffer, bytes); lastlba = last_lba(fd); if (!lastlba) return bytesread; /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. This is only used by gpt.c, and only to read one sector, so we don't have to be fancy. */ if (!bytesread && !(lastlba & 1) && lba == lastlba) { bytesread = read_lastoddsector(fd, lba, buffer, bytes); } return bytesread; } /** * alloc_read_gpt_entries(): reads partition entries from disk * @fd is an open file descriptor to the whole disk * @gpt is a buffer into which the GPT will be put * Description: Returns ptes on success, NULL on error. * Allocates space for PTEs based on information found in @gpt. * Notes: remember to free pte when you're done! */ static gpt_entry * alloc_read_gpt_entries(int fd, gpt_header * gpt) { gpt_entry *pte; size_t count = __le32_to_cpu(gpt->num_partition_entries) * __le32_to_cpu(gpt->sizeof_partition_entry); if (!count) return NULL; pte = (gpt_entry *)malloc(count); if (!pte) return NULL; memset(pte, 0, count); if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte, count)) { free(pte); return NULL; } return pte; } /** * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk * @fd is an open file descriptor to the whole disk * @lba is the Logical Block Address of the partition table * * Description: returns GPT header on success, NULL on error. Allocates * and fills a GPT header starting at @ from @bdev. * Note: remember to free gpt when finished with it. */ static gpt_header * alloc_read_gpt_header(int fd, uint64_t lba) { gpt_header *gpt; gpt = (gpt_header *) malloc(sizeof (gpt_header)); if (!gpt) return NULL; memset(gpt, 0, sizeof (*gpt)); if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) { free(gpt); return NULL; } return gpt; } /** * is_gpt_valid() - tests one GPT header and PTEs for validity * @fd is an open file descriptor to the whole disk * @lba is the logical block address of the GPT header to test * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * * Description: returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. */ static int is_gpt_valid(int fd, uint64_t lba, gpt_header ** gpt, gpt_entry ** ptes) { int rc = 0; /* default to not valid */ uint32_t crc, origcrc; if (!gpt || !ptes) return 0; if (!(*gpt = alloc_read_gpt_header(fd, lba))) return 0; /* Check the GUID Partition Table signature */ if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { /* printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n", __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE); */ free(*gpt); *gpt = NULL; return rc; } /* Check the GUID Partition Table Header CRC */ origcrc = __le32_to_cpu((*gpt)->header_crc32); (*gpt)->header_crc32 = 0; crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size)); if (crc != origcrc) { // printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc); (*gpt)->header_crc32 = __cpu_to_le32(origcrc); free(*gpt); *gpt = NULL; return 0; } (*gpt)->header_crc32 = __cpu_to_le32(origcrc); /* Check that the my_lba entry points to the LBA * that contains the GPT we read */ if (__le64_to_cpu((*gpt)->my_lba) != lba) { /* printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", __le64_to_cpu((*gpt)->my_lba), lba); */ free(*gpt); *gpt = NULL; return 0; } /* Check that sizeof_partition_entry has the correct value */ if (__le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { // printf("GUID partition entry size check failed.\n"); free(*gpt); *gpt = NULL; return 0; } /* Check that sizeof_partition_entry has the correct value */ if (__le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { // printf("GUID partition entry size check failed.\n"); free(*gpt); *gpt = NULL; return 0; } if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { free(*gpt); *gpt = NULL; return 0; } /* Check the GUID Partition Entry Array CRC */ crc = efi_crc32(*ptes, __le32_to_cpu((*gpt)->num_partition_entries) * __le32_to_cpu((*gpt)->sizeof_partition_entry)); if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) { // printf("GUID Partitition Entry Array CRC check failed.\n"); free(*gpt); *gpt = NULL; free(*ptes); *ptes = NULL; return 0; } /* We're done, all's well */ return 1; } /** * compare_gpts() - Search disk for valid GPT headers and PTEs * @pgpt is the primary GPT header * @agpt is the alternate GPT header * @lastlba is the last LBA number * Description: Returns nothing. Sanity checks pgpt and agpt fields * and prints warnings on discrepancies. * */ static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba) { int error_found = 0; if (!pgpt || !agpt) return; if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) { error_found++; fprintf(stderr, "GPT:Primary header LBA != Alt. header alternate_lba\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->my_lba), __le64_to_cpu(agpt->alternate_lba)); #endif } if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) { error_found++; fprintf(stderr, "GPT:Primary header alternate_lba != Alt. header my_lba\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->alternate_lba), __le64_to_cpu(agpt->my_lba)); #endif } if (__le64_to_cpu(pgpt->first_usable_lba) != __le64_to_cpu(agpt->first_usable_lba)) { error_found++; fprintf(stderr, "GPT:first_usable_lbas don't match.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->first_usable_lba), __le64_to_cpu(agpt->first_usable_lba)); #endif } if (__le64_to_cpu(pgpt->last_usable_lba) != __le64_to_cpu(agpt->last_usable_lba)) { error_found++; fprintf(stderr, "GPT:last_usable_lbas don't match.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->last_usable_lba), __le64_to_cpu(agpt->last_usable_lba)); #endif } if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { error_found++; fprintf(stderr, "GPT:disk_guids don't match.\n"); } if (__le32_to_cpu(pgpt->num_partition_entries) != __le32_to_cpu(agpt->num_partition_entries)) { error_found++; fprintf(stderr, "GPT:num_partition_entries don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->num_partition_entries), __le32_to_cpu(agpt->num_partition_entries)); } if (__le32_to_cpu(pgpt->sizeof_partition_entry) != __le32_to_cpu(agpt->sizeof_partition_entry)) { error_found++; fprintf(stderr, "GPT:sizeof_partition_entry values don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->sizeof_partition_entry), __le32_to_cpu(agpt->sizeof_partition_entry)); } if (__le32_to_cpu(pgpt->partition_entry_array_crc32) != __le32_to_cpu(agpt->partition_entry_array_crc32)) { error_found++; fprintf(stderr, "GPT:partition_entry_array_crc32 values don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->partition_entry_array_crc32), __le32_to_cpu(agpt->partition_entry_array_crc32)); } if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) { error_found++; fprintf(stderr, "GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->alternate_lba), lastlba); #endif } if (__le64_to_cpu(agpt->my_lba) != lastlba) { error_found++; fprintf(stderr, "GPT:Alternate GPT header not at the end of the disk.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(agpt->my_lba), lastlba); #endif } if (error_found) fprintf(stderr, "GPT: Use GNU Parted to correct GPT errors.\n"); return; } /** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @fd is an open file descriptor to the whole disk * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ static int find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) { extern int force_gpt; int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr = NULL; uint64_t lastlba; if (!gpt || !ptes) return 0; if (!(lastlba = last_lba(fd))) return 0; good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { good_agpt = is_gpt_valid(fd, __le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt) { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } } else { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) { goto fail; } /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = malloc(sizeof (*legacymbr)); if (legacymbr) { memset(legacymbr, 0, sizeof (*legacymbr)); read_lba(fd, 0, (uint8_t *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); free(legacymbr); legacymbr=NULL; } /* Failure due to bad PMBR */ if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) { fprintf(stderr, " Warning: Disk has a valid GPT signature " "but invalid PMBR.\n" " Assuming this disk is *not* a GPT disk anymore.\n" " Use gpt kernel option to override. " "Use GNU Parted to correct disk.\n"); goto fail; } /* Would fail due to bad PMBR, but force GPT anyhow */ if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) { fprintf(stderr, " Warning: Disk has a valid GPT signature but " "invalid PMBR.\n" " Use GNU Parted to correct disk.\n" " gpt option taken, disk treated as GPT.\n"); } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt && (good_pmbr || force_gpt)) { *gpt = pgpt; *ptes = pptes; if (agpt) { free(agpt); agpt = NULL; } if (aptes) { free(aptes); aptes = NULL; } if (!good_agpt) { fprintf(stderr, "Alternate GPT is invalid, " "using primary GPT.\n"); } return 1; } else if (good_agpt && (good_pmbr || force_gpt)) { *gpt = agpt; *ptes = aptes; if (pgpt) { free(pgpt); pgpt = NULL; } if (pptes) { free(pptes); pptes = NULL; } fprintf(stderr, "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: if (pgpt) { free(pgpt); pgpt=NULL; } if (agpt) { free(agpt); agpt=NULL; } if (pptes) { free(pptes); pptes=NULL; } if (aptes) { free(aptes); aptes=NULL; } *gpt = NULL; *ptes = NULL; return 0; } /** * read_gpt_pt() * @fd * @all - slice with start/size of whole disk * * 0 if this isn't our partition table * number of partitions if successful * */ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns) { gpt_header *gpt = NULL; gpt_entry *ptes = NULL; uint32_t i; int n = 0; int last_used_index=-1; int sector_size_mul = get_sector_size(fd)/512; if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) { if (gpt) free (gpt); if (ptes) free (ptes); return 0; } for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) { if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) { sp[n].start = 0; sp[n].size = 0; n++; } else { sp[n].start = sector_size_mul * __le64_to_cpu(ptes[i].starting_lba); sp[n].size = sector_size_mul * (__le64_to_cpu(ptes[i].ending_lba) - __le64_to_cpu(ptes[i].starting_lba) + 1); last_used_index=n; n++; } } free (ptes); free (gpt); return last_used_index+1; } multipath-tools-0.5.0+git1.656f8865/kpartx/gpt.h000066400000000000000000000073621260771327300210350ustar00rootroot00000000000000/* gpt.[ch] Copyright (C) 2000-2001 Dell Computer Corporation EFI GUID Partition Table handling Per Intel EFI Specification v1.02 http://developer.intel.com/technology/efi/efi.htm This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _GPT_H #define _GPT_H #include #include "kpartx.h" #include "dos.h" #include "efi.h" #define EFI_PMBR_OSTYPE_EFI 0xEF #define EFI_PMBR_OSTYPE_EFI_GPT 0xEE #define MSDOS_MBR_SIGNATURE 0xaa55 #define GPT_BLOCK_SIZE 512 #define GPT_HEADER_SIGNATURE 0x5452415020494645ULL #define GPT_HEADER_REVISION_V1_02 0x00010200 #define GPT_HEADER_REVISION_V1_00 0x00010000 #define GPT_HEADER_REVISION_V0_99 0x00009900 #define GPT_PRIMARY_PARTITION_TABLE_LBA 1 typedef struct _gpt_header { uint64_t signature; uint32_t revision; uint32_t header_size; uint32_t header_crc32; uint32_t reserved1; uint64_t my_lba; uint64_t alternate_lba; uint64_t first_usable_lba; uint64_t last_usable_lba; efi_guid_t disk_guid; uint64_t partition_entry_lba; uint32_t num_partition_entries; uint32_t sizeof_partition_entry; uint32_t partition_entry_array_crc32; uint8_t reserved2[GPT_BLOCK_SIZE - 92]; } __attribute__ ((packed)) gpt_header; typedef struct _gpt_entry_attributes { uint64_t required_to_function:1; uint64_t reserved:47; uint64_t type_guid_specific:16; } __attribute__ ((packed)) gpt_entry_attributes; typedef struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; uint64_t starting_lba; uint64_t ending_lba; gpt_entry_attributes attributes; efi_char16_t partition_name[72 / sizeof(efi_char16_t)]; } __attribute__ ((packed)) gpt_entry; /* These values are only defaults. The actual on-disk structures may define different sizes, so use those unless creating a new GPT disk! */ #define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384 /* Number of actual partition entries should be calculated as: */ #define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \ (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \ sizeof(gpt_entry)) /* Protected Master Boot Record & Legacy MBR share same structure */ /* Needs to be packed because the u16s force misalignment. */ typedef struct _legacy_mbr { uint8_t bootcode[440]; uint32_t unique_mbr_signature; uint16_t unknown; struct partition partition[4]; uint16_t signature; } __attribute__ ((packed)) legacy_mbr; #define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1 /* Functions */ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns); #endif /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-indent-level: 4 * c-brace-imaginary-offset: 0 * c-brace-offset: -4 * c-argdecl-indent: 4 * c-label-offset: -4 * c-continued-statement-offset: 4 * c-continued-brace-offset: 0 * indent-tabs-mode: nil * tab-width: 8 * End: */ multipath-tools-0.5.0+git1.656f8865/kpartx/kpartx.8000066400000000000000000000031031260771327300214610ustar00rootroot00000000000000.TH KPARTX 8 "July 2006" "" "Linux Administrator's Manual" .SH NAME kpartx \- Create device maps from partition tables .SH SYNOPSIS .B kpartx .RB [\| \-a\ \c .BR |\ -d\ |\ -l \|] .RB [\| \-v \|] .RB wholedisk .SH DESCRIPTION This tool, derived from util-linux' partx, reads partition tables on specified device and create device maps over partitions segments detected. It is called from hotplug upon device maps creation and deletion. .SH OPTIONS .TP .B \-a Add partition mappings .TP .B \-r Read-only partition mappings .TP .B \-d Delete partition mappings .TP .B \-u Update partition mappings .TP .B \-l List partition mappings that would be added \-a .TP .B \-p set device name-partition number delimiter .TP .B \-f force creation of mappings; overrides 'no_partitions' feature .TP .B \-g force GUID partition table (GPT) .TP .B \-v Operate verbosely .TP .B \-s Sync mode. Don't return until the partitions are created .SH EXAMPLE To mount all the partitions in a raw disk image: .IP kpartx \-av disk.img .PP This will output lines such as: .IP loop3p1 : 0 20964762 /dev/loop3 63 .PP The .I loop3p1 is the name of a device file under .I /dev/mapper which you can use to access the partition, for example to fsck it: .IP fsck /dev/mapper/loop3p1 .PP When you're done, you need to remove the devices: .IP kpartx \-d disk.img .SH "SEE ALSO" .BR multipath (8) .BR multipathd (8) .BR hotplug (8) .SH "AUTHORS" This man page was assembled By Patrick Caulfield for the Debian project. From documentation provided by the multipath author Christophe Varoqui, and others. multipath-tools-0.5.0+git1.656f8865/kpartx/kpartx.c000066400000000000000000000330201260771327300215350ustar00rootroot00000000000000/* * Source: copy of util-linux' partx partx.c * * Copyrights of the original file applies * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda * Copyright (c) 2005 Lars Soltau */ /* * Given a block device and a partition table type, * try to parse the partition table, and list the * contents. Optionally add or remove partitions. * * Read wholedisk and add all partitions: * kpartx [-a|-d|-l] [-v] wholedisk * * aeb, 2000-03-21 * cva, 2002-10-26 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "devmapper.h" #include "crc32.h" #include "lopart.h" #include "kpartx.h" #define SIZE(a) (sizeof(a)/sizeof((a)[0])) #define READ_SIZE 1024 #define MAXTYPES 64 #define MAXSLICES 256 #define DM_TARGET "linear" #define LO_NAME_SIZE 64 #define PARTNAME_SIZE 128 #define DELIM_SIZE 8 struct slice slices[MAXSLICES]; enum action { LIST, ADD, DELETE, UPDATE }; struct pt { char *type; ptreader *fn; } pts[MAXTYPES]; int ptct = 0; int udev_sync = 0; static void addpts(char *t, ptreader f) { if (ptct >= MAXTYPES) { fprintf(stderr, "addpts: too many types\n"); exit(1); } pts[ptct].type = t; pts[ptct].fn = f; ptct++; } static void initpts(void) { addpts("gpt", read_gpt_pt); addpts("dos", read_dos_pt); addpts("bsd", read_bsd_pt); addpts("solaris", read_solaris_pt); addpts("unixware", read_unixware_pt); addpts("dasd", read_dasd_pt); addpts("mac", read_mac_pt); addpts("sun", read_sun_pt); addpts("ps3", read_ps3_pt); } static char short_opts[] = "rladfgvp:t:su"; /* Used in gpt.c */ int force_gpt=0; int force_devmap=0; static int usage(void) { printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n"); printf("\t-a add partition devmappings\n"); printf("\t-r devmappings will be readonly\n"); printf("\t-d del partition devmappings\n"); printf("\t-u update partition devmappings\n"); printf("\t-l list partitions devmappings that would be added by -a\n"); printf("\t-p set device name-partition number delimiter\n"); printf("\t-g force GUID partition table (GPT)\n"); printf("\t-f force devmap create\n"); printf("\t-v verbose\n"); printf("\t-s sync mode. Don't return until the partitions are created\n"); return 1; } static void set_delimiter (char * device, char * delimiter) { char * p = device; while (*(p++) != 0x0) continue; if (isdigit(*(p - 2))) *delimiter = 'p'; } static void strip_slash (char * device) { char * p = device; while (*(p++) != 0x0) { if (*p == '/') *p = '!'; } } static int find_devname_offset (char * device) { char *p, *q = NULL; p = device; while (*p++) if (*p == '/') q = p; return (int)(q - device) + 1; } static char * get_hotplug_device(void) { unsigned int major, minor, off, len; const char *mapname; char *devname = NULL; char *device = NULL; char *var = NULL; struct stat buf; var = getenv("ACTION"); if (!var || strcmp(var, "add")) return NULL; /* Get dm mapname for hotpluged device. */ if (!(devname = getenv("DEVNAME"))) return NULL; if (stat(devname, &buf)) return NULL; major = major(buf.st_rdev); minor = minor(buf.st_rdev); if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */ return NULL; off = find_devname_offset(devname); len = strlen(mapname); /* Dirname + mapname + \0 */ if (!(device = (char *)malloc(sizeof(char) * (off + len + 1)))) return NULL; /* Create new device name. */ snprintf(device, off + 1, "%s", devname); snprintf(device + off, len + 1, "%s", mapname); if (strlen(device) != (off + len)) return NULL; return device; } int main(int argc, char **argv){ int i, j, m, n, op, off, arg, c, d, ro=0; int fd = -1; struct slice all; struct pt *ptp; enum action what = LIST; char *type, *diskdevice, *device, *progname; int verbose = 0; char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; char * loopdev = NULL; char * delim = NULL; char *uuid = NULL; char *mapname = NULL; int hotplug = 0; int loopcreated = 0; struct stat buf; initpts(); init_crc32(); type = device = diskdevice = NULL; memset(&all, 0, sizeof(all)); memset(&partname, 0, sizeof(partname)); /* Check whether hotplug mode. */ progname = strrchr(argv[0], '/'); if (!progname) progname = argv[0]; else progname++; if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */ hotplug = 1; /* Setup for original kpartx variables */ if (!(device = get_hotplug_device())) exit(1); diskdevice = device; what = ADD; } else if (argc < 2) { usage(); exit(1); } while ((arg = getopt(argc, argv, short_opts)) != EOF) switch(arg) { case 'r': ro=1; break; case 'f': force_devmap=1; break; case 'g': force_gpt=1; break; case 't': type = optarg; break; case 'v': verbose = 1; break; case 'p': delim = optarg; break; case 'l': what = LIST; break; case 'a': what = ADD; break; case 'd': what = DELETE; break; case 's': udev_sync = 1; break; case 'u': what = UPDATE; break; default: usage(); exit(1); } #ifdef LIBDM_API_COOKIE if (!udev_sync) dm_udev_set_sync_support(0); else dm_udev_set_sync_support(1); #endif if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE || what == UPDATE)) { fprintf(stderr, "device mapper prerequisites not met\n"); exit(1); } if (hotplug) { /* already got [disk]device */ } else if (optind == argc-2) { device = argv[optind]; diskdevice = argv[optind+1]; } else if (optind == argc-1) { diskdevice = device = argv[optind]; } else { usage(); exit(1); } if (stat(device, &buf)) { printf("failed to stat() %s\n", device); exit (1); } if (S_ISREG (buf.st_mode)) { /* already looped file ? */ loopdev = find_loop_by_file(device); if (!loopdev && what == DELETE) exit (0); if (!loopdev) { loopdev = find_unused_loop_device(); if (set_loop(loopdev, device, 0, &ro)) { fprintf(stderr, "can't set up loop\n"); exit (1); } loopcreated = 1; } device = loopdev; } off = find_devname_offset(device); if (!loopdev) { uuid = dm_mapuuid(major(buf.st_rdev), minor(buf.st_rdev)); mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); } if (!uuid) uuid = device + off; if (!mapname) mapname = device + off; else if (!force_devmap && dm_no_partitions(major(buf.st_rdev), minor(buf.st_rdev))) { /* Feature 'no_partitions' is set, return */ return 0; } if (delim == NULL) { delim = malloc(DELIM_SIZE); memset(delim, 0, DELIM_SIZE); set_delimiter(mapname, delim); } fd = open(device, O_RDONLY); if (fd == -1) { perror(device); exit(1); } /* add/remove partitions to the kernel devmapper tables */ int r = 0; for (i = 0; i < ptct; i++) { ptp = &pts[i]; if (type && strcmp(type, ptp->type)) continue; /* here we get partitions */ n = ptp->fn(fd, all, slices, SIZE(slices)); #ifdef DEBUG if (n >= 0) printf("%s: %d slices\n", ptp->type, n); #endif if (n > 0) { close(fd); fd = -1; } else continue; switch(what) { case LIST: for (j = 0, c = 0, m = 0; j < n; j++) { if (slices[j].size == 0) continue; if (slices[j].container > 0) { c++; continue; } slices[j].minor = m++; printf("%s%s%d : 0 %" PRIu64 " %s %" PRIu64"\n", mapname, delim, j+1, slices[j].size, device, slices[j].start); } /* Loop to resolve contained slices */ d = c; while (c) { for (j = 0; j < n; j++) { uint64_t start; int k = slices[j].container - 1; if (slices[j].size == 0) continue; if (slices[j].minor > 0) continue; if (slices[j].container == 0) continue; slices[j].minor = m++; start = slices[j].start - slices[k].start; printf("%s%s%d : 0 %" PRIu64 " /dev/dm-%d %" PRIu64 "\n", mapname, delim, j+1, slices[j].size, slices[k].minor, start); c--; } /* Terminate loop if nothing more to resolve */ if (d == c) break; } break; case DELETE: for (j = MAXSLICES-1; j >= 0; j--) { if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } strip_slash(partname); if (!dm_map_present(partname)) continue; if (!dm_simplecmd(DM_DEVICE_REMOVE, partname, 0, 0)) { r++; continue; } if (verbose) printf("del devmap : %s\n", partname); } if (S_ISREG (buf.st_mode)) { if (del_loop(device)) { if (verbose) printf("can't del loop : %s\n", device); exit(1); } printf("loop deleted : %s\n", device); } break; case ADD: case UPDATE: /* ADD and UPDATE share the same code that adds new partitions. */ for (j = 0, c = 0; j < n; j++) { if (slices[j].size == 0) continue; /* Skip all contained slices */ if (slices[j].container > 0) { c++; continue; } if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } strip_slash(partname); if (safe_sprintf(params, "%s %" PRIu64 , device, slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } op = (dm_map_present(partname) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); if (!dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid)) { fprintf(stderr, "create/reload failed on %s\n", partname); r++; } if (op == DM_DEVICE_RELOAD && !dm_simplecmd(DM_DEVICE_RESUME, partname, 1, MPATH_UDEV_RELOAD_FLAG)) { fprintf(stderr, "resume failed on %s\n", partname); r++; } dm_devn(partname, &slices[j].major, &slices[j].minor); if (verbose) printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n", partname, slices[j].major, slices[j].minor, slices[j].size, DM_TARGET, params); } /* Loop to resolve contained slices */ d = c; while (c) { for (j = 0; j < n; j++) { int k = slices[j].container - 1; if (slices[j].size == 0) continue; /* Skip all existing slices */ if (slices[j].minor > 0) continue; /* Skip all simple slices */ if (slices[j].container == 0) continue; /* Check container slice */ if (slices[k].size == 0) fprintf(stderr, "Invalid slice %d\n", k); if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } strip_slash(partname); if (safe_sprintf(params, "%s %" PRIu64, device, slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } op = (dm_map_present(partname) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, partname, 1, MPATH_UDEV_RELOAD_FLAG); dm_devn(partname, &slices[j].major, &slices[j].minor); if (verbose) printf("add map %s : 0 %" PRIu64 " %s %s\n", partname, slices[j].size, DM_TARGET, params); c--; } /* Terminate loop */ if (d == c) break; } if (what == ADD) { /* Skip code that removes devmappings for deleted partitions */ break; } for (j = MAXSLICES-1; j >= 0; j--) { if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } strip_slash(partname); if (slices[j].size || !dm_map_present(partname)) continue; if (!dm_simplecmd(DM_DEVICE_REMOVE, partname, 1, 0)) { r++; continue; } if (verbose) printf("del devmap : %s\n", partname); } default: break; } if (n > 0) break; } if (what == LIST && loopcreated && S_ISREG (buf.st_mode)) { if (fd != -1) close(fd); if (del_loop(device)) { if (verbose) printf("can't del loop : %s\n", device); exit(1); } printf("loop deleted : %s\n", device); } dm_lib_release(); dm_lib_exit(); return r; } void * xmalloc (size_t size) { void *t; if (size == 0) return NULL; t = malloc (size); if (t == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } return t; } /* * sseek: seek to specified sector */ static int sseek(int fd, unsigned int secnr) { off64_t in, out; in = ((off64_t) secnr << 9); out = 1; if ((out = lseek64(fd, in, SEEK_SET)) != in) { fprintf(stderr, "llseek error\n"); return -1; } return 0; } static struct block { unsigned int secnr; char *block; struct block *next; } *blockhead; char * getblock (int fd, unsigned int secnr) { struct block *bp; for (bp = blockhead; bp; bp = bp->next) if (bp->secnr == secnr) return bp->block; if (sseek(fd, secnr)) return NULL; bp = xmalloc(sizeof(struct block)); bp->secnr = secnr; bp->next = blockhead; blockhead = bp; bp->block = (char *) xmalloc(READ_SIZE); if (read(fd, bp->block, READ_SIZE) != READ_SIZE) { fprintf(stderr, "read error, sector %d\n", secnr); bp->block = NULL; } return bp->block; } int get_sector_size(int filedes) { int rc, sector_size = 512; rc = ioctl(filedes, BLKSSZGET, §or_size); if (rc) sector_size = 512; return sector_size; } multipath-tools-0.5.0+git1.656f8865/kpartx/kpartx.h000066400000000000000000000025211260771327300215440ustar00rootroot00000000000000#ifndef _KPARTX_H #define _KPARTX_H #include #include /* * For each partition type there is a routine that takes * a block device and a range, and returns the list of * slices found there in the supplied array SP that can * hold NS entries. The return value is the number of * entries stored, or -1 if the appropriate type is not * present. */ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define safe_sprintf(var, format, args...) \ snprintf(var, sizeof(var), format, ##args) >= sizeof(var) #ifndef BLKSSZGET #define BLKSSZGET _IO(0x12,104) /* get block device sector size */ #endif int get_sector_size(int filedes); /* * units: 512 byte sectors */ struct slice { uint64_t start; uint64_t size; int container; int major; int minor; }; typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); extern ptreader read_dos_pt; extern ptreader read_bsd_pt; extern ptreader read_solaris_pt; extern ptreader read_unixware_pt; extern ptreader read_gpt_pt; extern ptreader read_dasd_pt; extern ptreader read_mac_pt; extern ptreader read_sun_pt; extern ptreader read_ps3_pt; char *getblock(int fd, unsigned int secnr); static inline int four2int(unsigned char *p) { return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24); } #endif /* _KPARTX_H */ multipath-tools-0.5.0+git1.656f8865/kpartx/kpartx.rules000066400000000000000000000034031260771327300224470ustar00rootroot00000000000000# # persistent links for device-mapper devices # only hardware-backed device-mapper devices (ie multipath, dmraid, # and kpartx) have meaningful persistent device names # KERNEL!="dm-*", GOTO="kpartx_end" ACTION=="remove", GOTO="kpartx_end" ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end" ENV{DM_DEPS}=="0", GOTO="kpartx_end" ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" OPTIONS="link_priority=50" # Create persistent links for multipath tables ENV{DM_UUID}=="mpath-*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" ENV{DM_MPATH}=="?*", ENV{DM_PART}!="?*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}" ENV{DM_WWN}=="?*", ENV{DM_PART}!="?*", \ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}" # Create persistent links for partitions ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}" ENV{DM_MPATH}=="?*", ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}-part$env{DM_PART}" ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}" # Create persistent by-label/by-uuid links ENV{ID_FS_USAGE}=="?*", IMPORT{db}="ID_FS_USAGE" ENV{ID_FS_UUID_ENC}=="?*", IMPORT{db}="ID_FS_UUID_ENC" ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", \ SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" ENV{ID_FS_LABEL_ENC}=="?*", IMPORT{db}="ID_FS_LABEL_ENC" ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", \ SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" # Create dm tables for partitions ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", GOTO="kpartx_end" ENV{DM_NR_VALID_PATHS}=="0", GOTO="kpartx_end" ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \ RUN+="/sbin/kpartx -u -p -part /dev/$name" LABEL="kpartx_end" multipath-tools-0.5.0+git1.656f8865/kpartx/kpartx_id000066400000000000000000000050251260771327300217740ustar00rootroot00000000000000#!/bin/sh # # kpartx_id # # Generates ID information for device-mapper tables. # # Copyright (C) 2006 SUSE Linux Products GmbH # Author: # Hannes Reinecke # # # 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 version 2 of the License. # # This script generates ID information used to generate persistent symlinks. # It relies on the UUID strings generated by the various programs; the name # of the tables are of no consequence. # # Please note that dmraid does not provide the UUIDs (yet); a patch has been # sent upstream but has not been accepted yet. # DMSETUP=/sbin/dmsetup MAJOR=$1 MINOR=$2 UUID=$3 if [ -z "$MAJOR" -o -z "$MINOR" ]; then echo "usage: $0 major minor" exit 1; fi # Device-mapper not installed; not an error if [ ! -x $DMSETUP ] ; then exit 0 fi # Table UUIDs are always '-'. dmuuid=${UUID#*-} dmtbl=${UUID%%-*} dmpart=${dmtbl#part} # kpartx types are 'part' if [ "$dmpart" = "$dmtbl" ] ; then dmpart= else dmtbl=part fi # Set the name of the table. We're only interested in dmraid, # multipath, and kpartx tables; everything else is ignored. if [ "$dmtbl" = "part" ] ; then # The name of the kpartx table is the name of the parent table dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) echo "DM_NAME=$dmname" if [ "$dmname" != ${dmuuid#mpath-} ] ; then echo "DM_MPATH=${dmuuid#mpath-}" fi # We need the dependencies of the parent table to figure out # the type if the parent is a multipath table case "$dmuuid" in mpath-*) dmdeps=$($DMSETUP deps -u $dmuuid) ;; esac elif [ "$dmtbl" = "mpath" ] ; then if [ -n "$DM_NAME" -a "$DM_NAME" != "$dmuuid" ] ; then echo "DM_MPATH=$dmuuid" fi dmname="$dmuuid" # We need the dependencies of the table to figure out the type dmdeps=$($DMSETUP deps -u $UUID) elif [ "$dmtbl" = "dmraid" ] ; then dmname=$tblname fi [ -n "$dmpart" ] && echo "DM_PART=$dmpart" # Figure out the type of the map. For non-multipath maps it's # always 'raid'. if [ -n "$dmdeps" ] ; then case "$dmdeps" in *\(94,*) echo "DM_TYPE=ccw" ;; *\(104,* | *\(105,* | *\(106,* | *\(107,* | *\(108,* | *\(109,* | *\(110,* | *\(112,*) echo "DM_TYPE=cciss" ;; *\(9*) echo "DM_TYPE=raid" ;; *) echo "DM_TYPE=scsi" echo "DM_WWN=0x${dmname#?}" ;; esac else echo "DM_TYPE=raid" fi exit 0 multipath-tools-0.5.0+git1.656f8865/kpartx/lopart.c000066400000000000000000000147121260771327300215340ustar00rootroot00000000000000/* Taken from Ted's losetup.c - Mitch */ /* Added vfs mount options - aeb - 960223 */ /* Removed lomount - aeb - 960224 */ /* 1999-02-22 Arkadiusz Mi¶kiewicz * - added Native Language Support * Sun Mar 21 1999 - Arnaldo Carvalho de Melo * - fixed strerr(errno) in gettext calls */ #define PROC_DEVICES "/proc/devices" /* * losetup.c - setup and control loop devices */ #include "kpartx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lopart.h" #include "xstrncpy.h" #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \ && !defined (__s390x__) #define int2ptr(x) ((void *) ((int) x)) #else #define int2ptr(x) ((void *) ((long) x)) #endif static char * xstrdup (const char *s) { char *t; if (s == NULL) return NULL; t = strdup (s); if (t == NULL) { fprintf(stderr, "not enough memory"); exit(1); } return t; } extern int is_loop_device (const char *device) { struct stat statbuf; int loopmajor; #if 1 loopmajor = 7; #else FILE *procdev; char line[100], *cp; loopmajor = 0; if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) { while (fgets (line, sizeof(line), procdev)) { if ((cp = strstr (line, " loop\n")) != NULL) { *cp='\0'; loopmajor=atoi(line); break; } } fclose(procdev); } #endif return (loopmajor && stat(device, &statbuf) == 0 && S_ISBLK(statbuf.st_mode) && major(statbuf.st_rdev) == loopmajor); } #define SIZE(a) (sizeof(a)/sizeof(a[0])) extern char * find_loop_by_file (const char * filename) { char dev[64]; char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" }; int i, j, fd; struct stat statbuf; struct loop_info loopinfo; dev_t file_dev; ino_t file_ino; if (stat (filename, &statbuf) != 0) { return NULL; } file_dev = statbuf.st_dev; file_ino = statbuf.st_ino; for (j = 0; j < SIZE(loop_formats); j++) { for (i = 0; i < 256; i++) { sprintf (dev, loop_formats[j], i); if (stat (dev, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) continue; fd = open (dev, O_RDONLY); if (fd < 0) break; if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) { close (fd); continue; } if (loopinfo.lo_device == file_dev && loopinfo.lo_inode == file_ino) { close (fd); return xstrdup(dev); /*found */ } close (fd); continue; } } return NULL; } extern char * find_unused_loop_device (void) { /* Just creating a device, say in /tmp, is probably a bad idea - people might have problems with backup or so. So, we just try /dev/loop[0-7]. */ char dev[20]; char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" }; int i, j, fd, first = 0, somedev = 0, someloop = 0, loop_known = 0; struct stat statbuf; struct loop_info loopinfo; FILE *procdev; if (stat("/dev/loop-control", &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { fd = open("/dev/loop-control", O_RDWR); if (fd >= 0) first = ioctl(fd, LOOP_CTL_GET_FREE); close(fd); if (first < 0) first = 0; } for (j = 0; j < SIZE(loop_formats); j++) { for(i = first; i < 256; i++) { sprintf(dev, loop_formats[j], i); if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { somedev++; fd = open (dev, O_RDONLY); if (fd >= 0) { if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0) someloop++; /* in use */ else if (errno == ENXIO) { close (fd); return xstrdup(dev);/* probably free */ } close (fd); } /* continue trying as long as devices exist */ continue; } break; } } /* Nothing found. Why not? */ if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) { char line[100]; while (fgets (line, sizeof(line), procdev)) if (strstr (line, " loop\n")) { loop_known = 1; break; } fclose(procdev); if (!loop_known) loop_known = -1; } if (!somedev) fprintf(stderr, "mount: could not find any device /dev/loop#"); else if (!someloop) { if (loop_known == 1) fprintf(stderr, "mount: Could not find any loop device.\n" " Maybe /dev/loop# has a wrong major number?"); else if (loop_known == -1) fprintf(stderr, "mount: Could not find any loop device, and, according to %s,\n" " this kernel does not know about the loop device.\n" " (If so, then recompile or `modprobe loop'.)", PROC_DEVICES); else fprintf(stderr, "mount: Could not find any loop device. Maybe this kernel does not know\n" " about the loop device (then recompile or `modprobe loop'), or\n" " maybe /dev/loop# has the wrong major number?"); } else fprintf(stderr, "mount: could not find any free loop device"); return 0; } extern int set_loop (const char *device, const char *file, int offset, int *loopro) { struct loop_info loopinfo; int fd, ffd, mode; mode = (*loopro ? O_RDONLY : O_RDWR); if ((ffd = open (file, mode)) < 0) { if (!*loopro && (errno == EROFS || errno == EACCES)) ffd = open (file, mode = O_RDONLY); if (ffd < 0) { perror (file); return 1; } } if ((fd = open (device, mode)) < 0) { perror (device); return 1; } *loopro = (mode == O_RDONLY); memset (&loopinfo, 0, sizeof (loopinfo)); xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE); loopinfo.lo_offset = offset; loopinfo.lo_encrypt_type = LO_CRYPT_NONE; loopinfo.lo_encrypt_key_size = 0; if (ioctl (fd, LOOP_SET_FD, int2ptr(ffd)) < 0) { perror ("ioctl: LOOP_SET_FD"); close (fd); close (ffd); return 1; } if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) { (void) ioctl (fd, LOOP_CLR_FD, 0); perror ("ioctl: LOOP_SET_STATUS"); close (fd); close (ffd); return 1; } close (fd); close (ffd); return 0; } extern int del_loop (const char *device) { int retries = 3; int fd; if ((fd = open (device, O_RDONLY)) < 0) { int errsv = errno; fprintf(stderr, "loop: can't delete device %s: %s\n", device, strerror (errsv)); return 1; } while (ioctl (fd, LOOP_CLR_FD, 0) < 0) { if (errno != EBUSY || retries-- <= 0) { perror ("ioctl: LOOP_CLR_FD"); close (fd); return 1; } fprintf(stderr, "loop: device %s still in use, retrying delete\n", device); sleep(1); continue; } close (fd); return 0; } multipath-tools-0.5.0+git1.656f8865/kpartx/lopart.h000066400000000000000000000003761260771327300215420ustar00rootroot00000000000000extern int verbose; extern int set_loop (const char *, const char *, int, int *); extern int del_loop (const char *); extern int is_loop_device (const char *); extern char * find_unused_loop_device (void); extern char * find_loop_by_file (const char *); multipath-tools-0.5.0+git1.656f8865/kpartx/mac.c000066400000000000000000000021771260771327300207750ustar00rootroot00000000000000#include "kpartx.h" #include "byteorder.h" #include #include #include "mac.h" int read_mac_pt(int fd, struct slice all, struct slice *sp, int ns) { struct mac_driver_desc *md; struct mac_partition *part; unsigned secsize; char *data; int blk, blocks_in_map; int n = 0; md = (struct mac_driver_desc *) getblock(fd, 0); if (md == NULL) return -1; if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) return -1; secsize = be16_to_cpu(md->block_size); data = getblock(fd, secsize/512); if (!data) return -1; part = (struct mac_partition *) (data + secsize%512); if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) return -1; blocks_in_map = be32_to_cpu(part->map_count); for (blk = 1; blk <= blocks_in_map && blk <= ns; ++blk, ++n) { int pos = blk * secsize; data = getblock(fd, pos/512); if (!data) return -1; part = (struct mac_partition *) (data + pos%512); if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) break; sp[n].start = be32_to_cpu(part->start_block) * (secsize/512); sp[n].size = be32_to_cpu(part->block_count) * (secsize/512); } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/mac.h000066400000000000000000000015531260771327300207770ustar00rootroot00000000000000#ifndef MAC_H #define MAC_H #include #define MAC_PARTITION_MAGIC 0x504d /* type field value for A/UX or other Unix partitions */ #define APPLE_AUX_TYPE "Apple_UNIX_SVR2" struct mac_partition { uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ uint16_t res1; uint32_t map_count; /* # blocks in partition map */ uint32_t start_block; /* absolute starting block # of partition */ uint32_t block_count; /* number of blocks in partition */ /* there is more stuff after this that we don't need */ }; #define MAC_DRIVER_MAGIC 0x4552 /* Driver descriptor structure, in block 0 */ struct mac_driver_desc { uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ uint16_t block_size; uint32_t block_count; /* ... more stuff */ }; #endif multipath-tools-0.5.0+git1.656f8865/kpartx/ps3.c000066400000000000000000000027501260771327300207370ustar00rootroot00000000000000#include "kpartx.h" #include "byteorder.h" #include #include #define SECTOR_SIZE 512 #define MAX_ACL_ENTRIES 8 #define MAX_PARTITIONS 8 #define MAGIC1 0x0FACE0FFULL #define MAGIC2 0xDEADFACEULL struct p_acl_entry { u_int64_t laid; u_int64_t rights; }; struct d_partition { u_int64_t p_start; u_int64_t p_size; struct p_acl_entry p_acl[MAX_ACL_ENTRIES]; }; struct disklabel { u_int8_t d_res1[16]; u_int64_t d_magic1; u_int64_t d_magic2; u_int64_t d_res2; u_int64_t d_res3; struct d_partition d_partitions[MAX_PARTITIONS]; u_int8_t d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition)- 0x30]; }; static int read_disklabel(int fd, struct disklabel *label) { unsigned char *data; int i; for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) { data = (unsigned char *) getblock(fd, i); if (!data) return 0; memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE); } return 1; } int read_ps3_pt(int fd, struct slice all, struct slice *sp, int ns) { struct disklabel label; int n = 0; int i; if (!read_disklabel(fd, &label)) return -1; if ((be64_to_cpu(label.d_magic1) != MAGIC1) || (be64_to_cpu(label.d_magic2) != MAGIC2)) return -1; for (i = 0; i < MAX_PARTITIONS; i++) { if (label.d_partitions[i].p_start && label.d_partitions[i].p_size) { sp[n].start = be64_to_cpu(label.d_partitions[i].p_start); sp[n].size = be64_to_cpu(label.d_partitions[i].p_size); n++; } } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/solaris.c000066400000000000000000000034221260771327300217030ustar00rootroot00000000000000#include "kpartx.h" #include #include #include /* time_t */ #define SOLARIS_X86_NUMSLICE 8 #define SOLARIS_X86_VTOC_SANE (0x600DDEEEUL) //typedef int daddr_t; /* or long - check */ struct solaris_x86_slice { unsigned short s_tag; /* ID tag of partition */ unsigned short s_flag; /* permision flags */ daddr_t s_start; /* start sector no of partition */ long s_size; /* # of blocks in partition */ }; struct solaris_x86_vtoc { unsigned long v_bootinfo[3]; /* info for mboot */ unsigned long v_sanity; /* to verify vtoc sanity */ unsigned long v_version; /* layout version */ char v_volume[8]; /* volume name */ unsigned short v_sectorsz; /* sector size in bytes */ unsigned short v_nparts; /* number of partitions */ unsigned long v_reserved[10]; /* free space */ struct solaris_x86_slice v_slice[SOLARIS_X86_NUMSLICE]; /* slice headers */ time_t timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp */ char v_asciilabel[128]; /* for compatibility */ }; int read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) { struct solaris_x86_vtoc *v; struct solaris_x86_slice *s; unsigned int offset = all.start; int i, n; char *bp; bp = getblock(fd, offset+1); /* 1 sector suffices */ if (bp == NULL) return -1; v = (struct solaris_x86_vtoc *) bp; if(v->v_sanity != SOLARIS_X86_VTOC_SANE) return -1; if(v->v_version != 1) { fprintf(stderr, "Cannot handle solaris version %ld vtoc\n", v->v_version); return 0; } for(i=0, n=0; iv_slice[i]; if (s->s_size == 0) continue; if (n < ns) { sp[n].start = offset + s->s_start; sp[n].size = s->s_size; n++; } else { fprintf(stderr, "solaris_x86_partition: too many slices\n"); break; } } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/sun.c000066400000000000000000000062721260771327300210420ustar00rootroot00000000000000/* * Lifted from util-linux' partx sun.c * * Copyrights of the original file apply * Copyright (c) 2007 Hannes Reinecke */ #include "kpartx.h" #include "byteorder.h" #include #include #include /* time_t */ #define SUN_DISK_MAGIC 0xDABE /* Disk magic number */ #define SUN_DISK_MAXPARTITIONS 8 struct __attribute__ ((packed)) sun_raw_part { u_int32_t start_cylinder; /* where the part starts... */ u_int32_t num_sectors; /* ...and it's length */ }; struct __attribute__ ((packed)) sun_part_info { u_int8_t spare1; u_int8_t id; /* Partition type */ u_int8_t spare2; u_int8_t flags; /* Partition flags */ }; struct __attribute__ ((packed)) sun_disk_label { char info[128]; /* Informative text string */ u_int8_t spare0[14]; struct sun_part_info infos[SUN_DISK_MAXPARTITIONS]; u_int8_t spare1[246]; /* Boot information etc. */ u_int16_t rspeed; /* Disk rotational speed */ u_int16_t pcylcount; /* Physical cylinder count */ u_int16_t sparecyl; /* extra sects per cylinder */ u_int8_t spare2[4]; /* More magic... */ u_int16_t ilfact; /* Interleave factor */ u_int16_t ncyl; /* Data cylinder count */ u_int16_t nacyl; /* Alt. cylinder count */ u_int16_t ntrks; /* Tracks per cylinder */ u_int16_t nsect; /* Sectors per track */ u_int8_t spare3[4]; /* Even more magic... */ struct sun_raw_part partitions[SUN_DISK_MAXPARTITIONS]; u_int16_t magic; /* Magic number */ u_int16_t csum; /* Label xor'd checksum */ }; /* Checksum Verification */ static int sun_verify_checksum (struct sun_disk_label *label) { u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1; u_int16_t csum = 0; while (ush >= (u_int16_t *)label) csum ^= *ush--; return !csum; } int read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) { struct sun_disk_label *l; struct sun_raw_part *s; unsigned int offset = all.start, end; int i, j, n; char *bp; bp = getblock(fd, offset); if (bp == NULL) return -1; l = (struct sun_disk_label *) bp; if(be16_to_cpu(l->magic) != SUN_DISK_MAGIC) return -1; if (!sun_verify_checksum(l)) { fprintf(stderr, "Corrupted Sun disk label\n"); return -1; } for(i=0, n=0; ipartitions[i]; if (n < ns) { sp[n].start = offset + be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks); sp[n].size = be32_to_cpu(s->num_sectors); n++; } else { fprintf(stderr, "sun_disklabel: too many slices\n"); break; } } /* * Convention has it that the SUN disklabel will always have * the 'c' partition spanning the entire disk. * So we have to check for contained slices. */ for(i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { if (sp[i].size == 0) continue; end = sp[i].start + sp[i].size; for(j = 0; j < SUN_DISK_MAXPARTITIONS; j ++) { if ( i == j ) continue; if (sp[j].size == 0) continue; if (sp[i].start < sp[j].start) { if (end > sp[j].start && end < sp[j].start + sp[j].size) { /* Invalid slice */ fprintf(stderr, "sun_disklabel: slice %d overlaps with %d\n", i , j); sp[i].size = 0; } } else { if (end <= sp[j].start + sp[j].size) { sp[i].container = j + 1; } } } } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/sysmacros.h000066400000000000000000000002631260771327300222570ustar00rootroot00000000000000/* versions to be used with > 16-bit dev_t - leave unused for now */ #ifndef major #define major(dev) ((dev) >> 8) #endif #ifndef minor #define minor(dev) ((dev) & 0xff) #endif multipath-tools-0.5.0+git1.656f8865/kpartx/unixware.c000066400000000000000000000052551260771327300220770ustar00rootroot00000000000000#include "kpartx.h" #include #define UNIXWARE_FS_UNUSED 0 #define UNIXWARE_NUMSLICE 16 #define UNIXWARE_DISKMAGIC (0xCA5E600D) #define UNIXWARE_DISKMAGIC2 (0x600DDEEE) struct unixware_slice { unsigned short s_label; /* label */ unsigned short s_flags; /* permission flags */ unsigned int start_sect; /* starting sector */ unsigned int nr_sects; /* number of sectors in slice */ }; struct unixware_disklabel { unsigned int d_type; /* drive type */ unsigned char d_magic[4]; /* the magic number */ unsigned int d_version; /* version number */ char d_serial[12]; /* serial number of the device */ unsigned int d_ncylinders; /* # of data cylinders per device */ unsigned int d_ntracks; /* # of tracks per cylinder */ unsigned int d_nsectors; /* # of data sectors per track */ unsigned int d_secsize; /* # of bytes per sector */ unsigned int d_part_start; /* # of first sector of this partition */ unsigned int d_unknown1[12]; /* ? */ unsigned int d_alt_tbl; /* byte offset of alternate table */ unsigned int d_alt_len; /* byte length of alternate table */ unsigned int d_phys_cyl; /* # of physical cylinders per device */ unsigned int d_phys_trk; /* # of physical tracks per cylinder */ unsigned int d_phys_sec; /* # of physical sectors per track */ unsigned int d_phys_bytes; /* # of physical bytes per sector */ unsigned int d_unknown2; /* ? */ unsigned int d_unknown3; /* ? */ unsigned int d_pad[8]; /* pad */ struct unixware_vtoc { unsigned char v_magic[4]; /* the magic number */ unsigned int v_version; /* version number */ char v_name[8]; /* volume name */ unsigned short v_nslices; /* # of slices */ unsigned short v_unknown1; /* ? */ unsigned int v_reserved[10]; /* reserved */ struct unixware_slice v_slice[UNIXWARE_NUMSLICE]; /* slice headers */ } vtoc; }; /* 408 */ int read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) { struct unixware_disklabel *l; struct unixware_slice *p; unsigned int offset = all.start; char *bp; int n = 0; bp = getblock(fd, offset+29); /* 1 sector suffices */ if (bp == NULL) return -1; l = (struct unixware_disklabel *) bp; if (four2int(l->d_magic) != UNIXWARE_DISKMAGIC || four2int(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) return -1; p = &l->vtoc.v_slice[1]; /* slice 0 is the whole disk. */ while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) { if (p->s_label == UNIXWARE_FS_UNUSED) /* nothing */; else if (n < ns) { sp[n].start = p->start_sect; sp[n].size = p->nr_sects; n++; } else { fprintf(stderr, "unixware_partition: too many slices\n"); break; } p++; } return n; } multipath-tools-0.5.0+git1.656f8865/kpartx/xstrncpy.c000066400000000000000000000003261260771327300221210ustar00rootroot00000000000000/* NUL-terminated version of strncpy() */ #include #include "xstrncpy.h" /* caller guarantees n > 0 */ void xstrncpy(char *dest, const char *src, size_t n) { strncpy(dest, src, n-1); dest[n-1] = 0; } multipath-tools-0.5.0+git1.656f8865/kpartx/xstrncpy.h000066400000000000000000000000751260771327300221270ustar00rootroot00000000000000extern void xstrncpy(char *dest, const char *src, size_t n); multipath-tools-0.5.0+git1.656f8865/libmpathpersist/000077500000000000000000000000001260771327300217635ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/libmpathpersist/Makefile000066400000000000000000000030571260771327300234300ustar00rootroot00000000000000# Makefile # BUILD = glibc include ../Makefile.inc INSTALL_PROGRAM = install SONAME=0 DEVLIB = libmpathpersist.so LIBS = $(DEVLIB).$(SONAME) CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o all: $(LIBS) $(LIBS): $(CC) -Wall -fPIC -c $(CFLAGS) *.c $(CC) -shared $(LIBDEPS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) ln -s $(LIBS) $(DEVLIB) $(GZIP) mpath_persistent_reserve_in.3 > mpath_persistent_reserve_in.3.gz $(GZIP) mpath_persistent_reserve_out.3 > mpath_persistent_reserve_out.3.gz install: $(LIBS) $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/include/ $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/share/doc/mpathpersist/ ln -sf $(DESTDIR)$(syslibdir)/$(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) install -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir) install -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir) install -m 644 mpath_persist.h $(DESTDIR)/usr/include/ uninstall: rm -f $(DESTDIR)$(syslibdir)/$(LIBS) rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz clean: rm -f core *.a *.o rm -f libmpathpersist.so.0 rm -f libmpathpersist.so rm -f mpath_persistent_reserve_in.3.gz mpath_persistent_reserve_out.3.gz multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_persist.c000066400000000000000000000544211260771327300250170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpath_persist.h" #include "mpathpr.h" #include "mpath_pr_ioctl.h" #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 int mpath_lib_init (struct udev *udev) { if (load_config(DEFAULT_CONFIGFILE, udev)){ condlog(0, "Failed to initialize multipath config."); return 1; } if (conf->max_fds) { struct rlimit fd_limit; fd_limit.rlim_cur = conf->max_fds; fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) condlog(0, "can't set open fds limit to %d : %s", conf->max_fds, strerror(errno)); } return 0; } int mpath_lib_exit (void) { dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); free_config(conf); conf = NULL; return 0; } static int updatepaths (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i){ if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j){ if (!strlen(pp->dev)){ if (devt2devname(pp->dev, PATH_SIZE, pp->dev_t)){ /* * path is not in sysfs anymore */ pp->state = PATH_DOWN; continue; } pp->mpp = mpp; pathinfo(pp, conf->hwtable, DI_ALL); continue; } pp->mpp = mpp; if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) pathinfo(pp, conf->hwtable, DI_CHECKER); if (pp->priority == PRIO_UNDEF) pathinfo(pp, conf->hwtable, DI_PRIO); } } return 0; } int mpath_prin_activepath (struct multipath *mpp, int rq_servact, struct prin_resp * resp, int noisy) { int i,j, ret = MPATH_PR_DMMP_ERROR; struct pathgroup *pgp = NULL; struct path *pp = NULL; vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog(2, "%s: %s not available. Skip.", mpp->wwid, pp->dev); condlog(3, "%s: status = %d.", mpp->wwid, pp->state); continue; } condlog(3, "%s: sending pr in command to %s ", mpp->wwid, pp->dev); ret = mpath_send_prin_activepath(pp->dev, rq_servact, resp, noisy); switch(ret) { case MPATH_PR_SUCCESS: case MPATH_PR_SENSE_INVALID_OP: return ret; default: continue; } } } return ret; } int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose) { struct stat info; vector curmp = NULL; vector pathvec = NULL; char * alias; struct multipath * mpp; int map_present; int major, minor; int ret; conf->verbosity = verbose; if (fstat( fd, &info) != 0){ condlog(0, "stat error %d", fd); return MPATH_PR_FILE_ERROR; } if(!S_ISBLK(info.st_mode)){ condlog(0, "Failed to get major:minor. fd = %d", fd); return MPATH_PR_FILE_ERROR; } major = major(info.st_rdev); minor = minor(info.st_rdev); condlog(4, "Device %d:%d: ", major, minor); /* get alias from major:minor*/ alias = dm_mapname(major, minor); if (!alias){ condlog(0, "%d:%d failed to get device alias.", major, minor); return MPATH_PR_DMMP_ERROR; } condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)){ condlog( 0, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc (); pathvec = vector_alloc (); if (!curmp || !pathvec){ condlog (0, "%s: vector allocation failed.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } /* get info of all paths from the dm device */ if (get_mpvec (curmp, pathvec, alias)){ condlog(0, "%s: failed to get device info.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } mpp = find_mp_by_alias(curmp, alias); if (!mpp){ condlog(0, "%s: devmap not registered.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy); out1: free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); out: FREE(alias); return ret; } int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) { struct stat info; vector curmp = NULL; vector pathvec = NULL; char * alias; struct multipath * mpp; int map_present; int major, minor; int ret; int j; unsigned char *keyp; uint64_t prkey; conf->verbosity = verbose; if (fstat( fd, &info) != 0){ condlog(0, "stat error fd=%d", fd); return MPATH_PR_FILE_ERROR; } if(!S_ISBLK(info.st_mode)){ condlog(3, "Failed to get major:minor. fd=%d", fd); return MPATH_PR_FILE_ERROR; } major = major(info.st_rdev); minor = minor(info.st_rdev); condlog(4, "Device %d:%d", major, minor); /* get WWN of the device from major:minor*/ alias = dm_mapname(major, minor); if (!alias){ return MPATH_PR_DMMP_ERROR; } condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)){ condlog(3, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc (); pathvec = vector_alloc (); if (!curmp || !pathvec){ condlog (0, "%s: vector allocation failed.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } /* get info of all paths from the dm device */ if (get_mpvec(curmp, pathvec, alias)){ condlog(0, "%s: failed to get device info.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } mpp = find_mp_by_alias(curmp, alias); if (!mpp) { condlog(0, "%s: devmap not registered.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } select_reservation_key(mpp); switch(rq_servact) { case MPATH_PROUT_REG_SA: case MPATH_PROUT_REG_IGN_SA: ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; case MPATH_PROUT_RES_SA : case MPATH_PROUT_PREE_SA : case MPATH_PROUT_PREE_AB_SA : case MPATH_PROUT_CLEAR_SA: ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; case MPATH_PROUT_REL_SA: ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; default: ret = MPATH_PR_OTHER; goto out1; } if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) || (rq_servact == MPATH_PROUT_REG_IGN_SA))) { keyp=paramp->sa_key; prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= *keyp; ++keyp; } if (prkey == 0) update_prflag(alias, "unset", noisy); else update_prflag(alias, "set", noisy); } else { if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) || (rq_servact == MPATH_PROUT_PREE_AB_SA ))){ update_prflag(alias, "unset", noisy); } } out1: free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); out: FREE(alias); return ret; } int get_mpvec (vector curmp, vector pathvec, char * refwwid) { int i; struct multipath *mpp; char params[PARAMS_SIZE], status[PARAMS_SIZE]; if (dm_get_maps (curmp)){ return 1; } vector_foreach_slot (curmp, mpp, i){ /* * discard out of scope maps */ if (mpp->alias && refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE)){ free_multipath (mpp, KEEP_PATHS); vector_del_slot (curmp, i); i--; continue; } dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); condlog(3, "status = %s", status); disassemble_map (pathvec, params, mpp); /* * disassemble_map() can add new paths to pathvec. * If not in "fast list mode", we need to fetch information * about them */ updatepaths(mpp); mpp->bestpg = select_path_group (mpp); disassemble_status (status, mpp); } return MPATH_PR_SUCCESS ; } void * mpath_prin_pthread_fn (void *p) { int ret; struct prin_param * pparam = (struct prin_param *)p; ret = prin_do_scsi_ioctl(pparam->dev, pparam->rq_servact, pparam->resp, pparam->noisy); pparam->status = ret; pthread_exit(NULL); } int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy) { int rc; rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy); return (rc); } int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { int i, j; struct pathgroup *pgp = NULL; struct path *pp = NULL; int rollback = 0; int active_pathcount=0; int rc; int count=0; int status = MPATH_PR_SUCCESS; uint64_t sa_key = 0; if (!mpp) return MPATH_PR_DMMP_ERROR; active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); if (active_pathcount == 0) { condlog (0, "%s: no path available", mpp->wwid); return MPATH_PR_DMMP_ERROR; } if ( paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK ) { condlog (1, "Warning: ALL_TG_PT is set. Configuration not supported"); } struct threadinfo thread[active_pathcount]; memset(thread, 0, sizeof(thread)); /* init thread parameter */ for (i =0; i< active_pathcount; i++){ thread[i].param.rq_servact = rq_servact; thread[i].param.rq_scope = rq_scope; thread[i].param.rq_type = rq_type; thread[i].param.paramp = paramp; thread[i].param.noisy = noisy; thread[i].param.status = -1; condlog (3, "THRED ID [%d] INFO]", i); condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); condlog (3, "rq_type=%d ", thread[i].param.rq_type); condlog (3, "rkey="); condlog (3, "paramp->sa_flags =%02x ", thread[i].param.paramp->sa_flags); condlog (3, "noisy=%d ", thread[i].param.noisy); condlog (3, "status=%d ", thread[i].param.status); } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev); continue; } strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE); if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){ /* * Clearing SPEC_I_PT as transportids are already registered by now. */ thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK); } condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param)); if (rc){ condlog (0, "%s: failed to create thread %d", mpp->wwid, rc); } count = count +1; } } for( i=0; i < active_pathcount ; i++){ rc = pthread_join(thread[i].id, NULL); if (rc){ condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc); } if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){ rollback = 1; sa_key = 0; for (i = 0; i < 8; ++i){ if (i > 0) sa_key <<= 8; sa_key |= paramp->sa_key[i]; } status = MPATH_PR_RESERV_CONFLICT ; } if (!rollback && (status == MPATH_PR_SUCCESS)){ status = thread[i].param.status; } } if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){ condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid); for( i=0 ; i < active_pathcount ; i++){ if((thread[i].param.status == MPATH_PR_SUCCESS) && ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8); memset(&thread[i].param.paramp->sa_key, 0, 8); thread[i].param.status = MPATH_PR_SUCCESS; rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[i].param)); if (rc){ condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc); } } } for(i=0; i < active_pathcount ; i++){ rc = pthread_join(thread[i].id, NULL); if (rc){ condlog (3, "%s: failed to join thread while rolling back %d", mpp->wwid, i); } } } pthread_attr_destroy(&attr); return (status); } void * mpath_prout_pthread_fn(void *p) { int ret; struct prout_param * param = (struct prout_param *)p; ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope, param->rq_type, param->paramp, param->noisy); param->status = ret; pthread_exit(NULL); } int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy) { int i,j, ret; struct pathgroup *pgp = NULL; struct path *pp = NULL; vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up. Skip", mpp->wwid, pp->dev); continue; } condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); ret = send_prout_activepath(pp->dev, rq_servact, rq_scope, rq_type, paramp, noisy); return ret ; } } return MPATH_PR_SUCCESS; } int send_prout_activepath(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { struct prout_param param; param.rq_servact = rq_servact; param.rq_scope = rq_scope; param.rq_type = rq_type; param.paramp = paramp; param.noisy = noisy; param.status = -1; pthread_t thread; pthread_attr_t attr; int rc; memset(&thread, 0, sizeof(thread)); strncpy(param.dev, dev, FILE_NAME_SIZE); /* Initialize and set thread joinable attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m)); if (rc){ condlog (3, "%s: failed to create thread %d", dev, rc); return MPATH_PR_OTHER; } /* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); rc = pthread_join(thread, NULL); return (param.status); } int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { int i, j; int num = 0; struct pathgroup *pgp = NULL; struct path *pp = NULL; int active_pathcount = 0; pthread_attr_t attr; int rc, found = 0;; int count = 0; int status = MPATH_PR_SUCCESS; struct prin_resp resp; struct prout_param_descriptor *pamp; struct prin_resp *pr_buff; int length; struct transportid *pptr; if (!mpp) return MPATH_PR_DMMP_ERROR; active_pathcount = pathcount (mpp, PATH_UP) + pathcount (mpp, PATH_GHOST); struct threadinfo thread[active_pathcount]; memset(thread, 0, sizeof(thread)); for (i = 0; i < active_pathcount; i++){ thread[i].param.rq_servact = rq_servact; thread[i].param.rq_scope = rq_scope; thread[i].param.rq_type = rq_type; thread[i].param.paramp = paramp; thread[i].param.noisy = noisy; thread[i].param.status = -1; condlog (3, " path count = %d", i); condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); condlog (3, "rq_type=%d ", thread[i].param.rq_type); condlog (3, "noisy=%d ", thread[i].param.noisy); condlog (3, "status=%d ", thread[i].param.status); } pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev); continue; } strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE); condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn, (void *) (&thread[count].param)); if (rc) condlog (0, "%s: failed to create thread. %d", mpp->wwid, rc); count = count + 1; } } pthread_attr_destroy (&attr); for (i = 0; i < active_pathcount; i++){ rc = pthread_join (thread[i].id, NULL); if (rc){ condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc); } } for (i = 0; i < active_pathcount; i++){ /* check thread status here and return the status */ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT) status = MPATH_PR_RESERV_CONFLICT; else if (status == MPATH_PR_SUCCESS && thread[i].param.status != MPATH_PR_RESERV_CONFLICT) status = thread[i].param.status; } status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy); if (status != MPATH_PR_SUCCESS){ condlog (0, "%s: pr in read reservation command failed.", mpp->wwid); return MPATH_PR_OTHER; } num = resp.prin_descriptor.prin_readresv.additional_length / 8; if (num == 0){ condlog (2, "%s: Path holding reservation is released.", mpp->wwid); return MPATH_PR_SUCCESS; } condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid); pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA); if (!pr_buff){ condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid); return MPATH_PR_OTHER; } status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy); if (status != MPATH_PR_SUCCESS){ condlog (0, "%s: pr in read full status command failed.", mpp->wwid); goto out; } num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; if (0 == num){ goto out; } length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *)); pamp = (struct prout_param_descriptor *)malloc (length); if (!pamp){ condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid); goto out1; } memset(pamp, 0, length); pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid)); if (!pamp->trnptid_list[0]){ condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid); goto out1; } if (mpp->reservation_key ){ memcpy (pamp->key, mpp->reservation_key, 8); condlog (3, "%s: reservation key set.", mpp->wwid); } status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA, rq_scope, rq_type, pamp, noisy); if (status) { condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid); goto out1; } pamp->num_transportid = 1; pptr=pamp->trnptid_list[0]; for (i = 0; i < num; i++){ if (mpp->reservation_key && memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, mpp->reservation_key, 8)){ /*register with tarnsport id*/ memset(pamp, 0, length); pamp->trnptid_list[0] = pptr; memset (pamp->trnptid_list[0], 0, sizeof (struct transportid)); memcpy (pamp->sa_key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8); pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK; pamp->num_transportid = 1; memcpy (pamp->trnptid_list[0], &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid, sizeof (struct transportid)); status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type, pamp, noisy); pamp->sa_flags = 0; memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8); memset (pamp->sa_key, 0, 8); pamp->num_transportid = 0; status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type, pamp, noisy); } else { if (mpp->reservation_key) found = 1; } } if (found){ memset (pamp, 0, length); memcpy (pamp->sa_key, mpp->reservation_key, 8); memset (pamp->key, 0, 8); status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy); } free(pptr); out1: free (pamp); out: free (pr_buff); return (status); } void * mpath_alloc_prin_response(int prin_sa) { void * ptr = NULL; int size=0; switch (prin_sa) { case MPATH_PRIN_RKEY_SA: size = sizeof(struct prin_readdescr); ptr = malloc(size); memset(ptr, 0, size); break; case MPATH_PRIN_RRES_SA: size = sizeof(struct prin_resvdescr); ptr = malloc(size); memset(ptr, 0, size); break; case MPATH_PRIN_RCAP_SA: size=sizeof(struct prin_capdescr); ptr = malloc(size); memset(ptr, 0, size); break; case MPATH_PRIN_RFSTAT_SA: size = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS; ptr = malloc(size); memset(ptr, 0, size); break; } return ptr; } int update_map_pr(struct multipath *mpp) { int noisy=0; struct prin_resp *resp; int i,j, ret, isFound; unsigned char *keyp; uint64_t prkey; if (!mpp->reservation_key) { /* Nothing to do. Assuming pr mgmt feature is disabled*/ condlog(3, "%s: reservation_key not set in multiapth.conf", mpp->alias); return MPATH_PR_SUCCESS; } resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA); if (!resp) { condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias); return MPATH_PR_OTHER; } ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret); free(resp); return ret; } if (resp->prin_descriptor.prin_readkeys.additional_length == 0 ) { condlog(0,"%s: No key found. Device may not be registered. ", mpp->alias); free(resp); return MPATH_PR_SUCCESS; } prkey = 0; keyp = mpp->reservation_key; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= *keyp; ++keyp; } condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias, prkey); isFound =0; for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ ) { condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i); dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1); if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8)) { condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias); isFound =1; } } if (isFound) { mpp->prflag = 1; condlog(2, "%s: prflag flag set.", mpp->alias ); } free(resp); return MPATH_PR_SUCCESS; } multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_persist.h000066400000000000000000000200331260771327300250140ustar00rootroot00000000000000/* version - 1.0 */ #ifndef MPATH_PERSIST_LIB_H #define MPATH_PERSIST_LIB_H #ifdef __cplusplus extern "C" { #endif #include #define MPATH_MAX_PARAM_LEN 8192 #define MPATH_MX_TIDS 32 /* Max number of transport ids"*/ #define MPATH_MX_TID_LEN 256 /* Max lenght of transport id */ /* PRIN Service Actions */ #define MPATH_PRIN_RKEY_SA 0x00 /* READ KEYS SA*/ #define MPATH_PRIN_RRES_SA 0x01 /* READ RESERVATION SA*/ #define MPATH_PRIN_RCAP_SA 0x02 /* REPORT CAPABILITIES SA*/ #define MPATH_PRIN_RFSTAT_SA 0x03 /* READ FULL STATUS SA*/ /* PROUT Service Actions */ #define MPATH_PROUT_REG_SA 0x00 /* REGISTER SA */ #define MPATH_PROUT_RES_SA 0x01 /* RESERVE SA*/ #define MPATH_PROUT_REL_SA 0x02 /* RELEASE SA*/ #define MPATH_PROUT_CLEAR_SA 0x03 /* CLEAR SA*/ #define MPATH_PROUT_PREE_SA 0x04 /* PREEMPT SA*/ #define MPATH_PROUT_PREE_AB_SA 0x05 /* PREEMPT AND ABORT SA*/ #define MPATH_PROUT_REG_IGN_SA 0x06 /* REGISTER AND IGNORE EXISTING KEY SA*/ #define MPATH_PROUT_REG_MOV_SA 0x07 /* REGISTER AND MOVE SA*/ #define MPATH_LU_SCOPE 0x00 /* LU_SCOPE */ /* Persistent reservations type */ #define MPATH_PRTPE_WE 0x01 /* Write Exclusive */ #define MPATH_PRTPE_EA 0x03 /* Exclusive Access*/ #define MPATH_PRTPE_WE_RO 0x05 /* WriteExclusive Registrants Only */ #define MPATH_PRTPE_EA_RO 0x06 /* Exclusive Access. Registrants Only*/ #define MPATH_PRTPE_WE_AR 0x07 /* Write Exclusive. All Registrants*/ #define MPATH_PRTPE_EA_AR 0x08 /* Exclusive Access. All Registrants */ /* PR RETURN_STATUS */ #define MPATH_PR_SUCCESS 0 #define MPATH_PR_SYNTAX_ERROR 1 /* syntax error or invalid parameter */ /* status for check condition */ #define MPATH_PR_SENSE_NOT_READY 2 /* [sk,asc,ascq: 0x2,*,*] */ #define MPATH_PR_SENSE_MEDIUM_ERROR 3 /* [sk,asc,ascq: 0x3,*,*] */ #define MPATH_PR_SENSE_HARDWARE_ERROR 4 /* [sk,asc,ascq: 0x4,*,*] */ #define MPATH_PR_ILLEGAL_REQ 5 /* [sk,asc,ascq: 0x5,*,*]*/ #define MPATH_PR_SENSE_UNIT_ATTENTION 6 /* [sk,asc,ascq: 0x6,*,*] */ #define MPATH_PR_SENSE_INVALID_OP 7 /* [sk,asc,ascq: 0x5,0x20,0x0]*/ #define MPATH_PR_SENSE_ABORTED_COMMAND 8 /* [sk,asc,ascq: 0xb,*,*] */ #define MPATH_PR_NO_SENSE 9 /* [sk,asc,ascq: 0x0,*,*] */ #define MPATH_PR_SENSE_MALFORMED 10 /* Response to SCSI command malformed */ #define MPATH_PR_RESERV_CONFLICT 11 /* Reservation conflict on the device */ #define MPATH_PR_FILE_ERROR 12 /* file (device node) problems(e.g. not found)*/ #define MPATH_PR_DMMP_ERROR 13 /* DMMP related error.(e.g Error in getting dm info */ #define MPATH_PR_OTHER 14 /*other error/warning has occurred(transport or driver error) */ /* PR MASK */ #define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/ #define MPATH_F_ALL_TG_PT_MASK 0x04 /* ALL_TG_PT MASK*/ #define MPATH_F_SPEC_I_PT_MASK 0x08 /* SPEC_I_PT MASK*/ #define MPATH_PR_TYPE_MASK 0x0f /* TYPE MASK*/ #define MPATH_PR_SCOPE_MASK 0xf0 /* SCOPE MASK*/ /*Transport ID PROTOCOL IDENTIFIER values */ #define MPATH_PROTOCOL_ID_FC 0x00 #define MPATH_PROTOCOL_ID_ISCSI 0x05 #define MPATH_PROTOCOL_ID_SAS 0x06 /*Transport ID FORMATE CODE */ #define MPATH_WWUI_DEVICE_NAME 0x00 /* World wide unique initiator device name */ #define MPATH_WWUI_PORT_IDENTIFIER 0x40 /* World wide unique initiator port identifier */ struct prin_readdescr { uint32_t prgeneration; uint32_t additional_length; /* The value should be either 0 or divisible by 8. 0 indicates no registered reservation key. */ uint8_t key_list[MPATH_MAX_PARAM_LEN]; }; struct prin_resvdescr { uint32_t prgeneration; uint32_t additional_length; /* The value should be either 0 or 10h. 0 indicates there is no reservation held. 10h indicates the key[8] and scope_type have valid values */ uint8_t key[8]; uint32_t _obsolete; uint8_t _reserved; uint8_t scope_type; /* Use PR SCOPE AND TYPE MASK specified above */ uint16_t _obsolete1; }; struct prin_capdescr { uint16_t length; uint8_t flags[2]; uint16_t pr_type_mask; uint16_t _reserved; }; struct transportid { uint8_t format_code; uint8_t protocol_id; union { uint8_t n_port_name[8]; /* FC transport*/ uint8_t sas_address[8]; /* SAS transport */ uint8_t iscsi_name[256]; /* ISCSI transport */ }; }; struct prin_fulldescr { uint8_t key[8]; uint8_t flag; /* All_tg_pt and reservation holder */ uint8_t scope_type; /* Use PR SCOPE AND TYPE MASK specified above. Meaningful only for reservation holder */ uint16_t rtpi; struct transportid trnptid; }; struct print_fulldescr_list { uint32_t prgeneration; uint32_t number_of_descriptor; uint8_t private_buffer[MPATH_MAX_PARAM_LEN]; /*Private buffer for list storage*/ struct prin_fulldescr *descriptors[]; }; struct prin_resp { union { struct prin_readdescr prin_readkeys; /* for PRIN read keys SA*/ struct prin_resvdescr prin_readresv; /* for PRIN read reservation SA*/ struct prin_capdescr prin_readcap; /* for PRIN Report Capabilities SA*/ struct print_fulldescr_list prin_readfd; /* for PRIN read full status SA*/ }prin_descriptor; }; struct prout_param_descriptor { /* PROUT parameter descriptor */ uint8_t key[8]; uint8_t sa_key[8]; uint32_t _obsolete; uint8_t sa_flags; uint8_t _reserved; uint16_t _obsolete1; uint8_t private_buffer[MPATH_MAX_PARAM_LEN]; /*private buffer for list storage*/ uint32_t num_transportid; /* Number of Transport ID listed in trnptid_list[]*/ struct transportid *trnptid_list[]; }; /* Function declarations */ /* * DESCRIPTION : * Initialize device mapper multipath configuration. This function must be invoked first * before performing reservation management functions. * RESTRICTIONS: * * RETURNS: 0->Success, 1->Failed. */ extern int mpath_lib_init (struct udev *udev); /* * DESCRIPTION : * Release device mapper multipath configuration. This function must be invoked after * performing reservation management functions. * RESTRICTIONS: * * RETURNS: 0->Success, 1->Failed. */ extern int mpath_lib_exit (void ); /* * DESCRIPTION : * This function sends PRIN command to the DM device and get the response. * * @fd: The file descriptor of a multipath device. Input argument. * @rq_servact: PRIN command service action. Input argument * @resp: The response from PRIN service action. The resp is a struct specified above. The caller should * manage the memory allocation of this struct * @noisy: Turn on debugging trace: Input argument. 0->Disable, 1->Enable * @verbose: Set verbosity level. Input argument. value:[0-3]. 0->disabled, 3->Max verbose * * RESTRICTIONS: * * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the status specified * above in RETURN_STATUS. * */ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose); /* * DESCRIPTION : * This function sends PROUT command to the DM device and get the response. * * @fd: The file descriptor of a multipath device. Input argument. * @rq_servact: PROUT command service action. Input argument * @rq_scope: Persistent reservation scope. The value should be always LU_SCOPE (0h). * @rq_type: Persistent reservation type. The valid values of persistent reservation types are * 5h (Write exclusive - registrants only) * 6h (Exclusive access - registrants only) * 7h (Write exclusive - All registrants) * 8h (Exclusive access - All registrants). * @paramp: PROUT command parameter data. The paramp is a struct which describes PROUT * parameter list. The caller should manage the memory allocation of this struct. * @noisy: Turn on debugging trace: Input argument.0->Disable, 1->Enable. * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose * * RESTRICTIONS: * * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the status specified * above in RETURN_STATUS. */ extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose); #ifdef __cplusplus } #endif #endif /*MPATH_PERSIST_LIB_H*/ multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_persistent_reserve_in.3000066400000000000000000000041741260771327300276670ustar00rootroot00000000000000.\" .TH MPATH_PERSISTENT_RESERVE_IN 3 2011-04-08 "Linux Manpage" .SH NAME mpath_persistent_reserve_in .SH SYNOPSIS .B #include .sp .BI "int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose)" .sp .SH DESCRIPTION The function in the .BR mpath_persistent_reserve_in () sends PRIN command to the DM device and gets the response. .br .BI Parameters: .br .I fd .B The file descriptor of a multipath device. Input argument. .br .I rq_servact .B PRIN command service action. Input argument .br .I resp .B The response from PRIN service action. The caller should manage the memory allocation of this structure .br .I noisy .B Turn on debugging trace: Input argument. 0->Disable, 1->Enable .br .I verbose .B Set verbosity level. Input argument. value:[0-3]. 0->Crits and Errors, 1->Warnings, 2->Info, 3->Debug .br .SH "RETURNS" .I MPATH_PR_SUCCESS .B if PR command successful .br .I MPATH_PR_SYNTAX_ERROR .B if syntax error or invalid parameter .br .I MPATH_PR_SENSE_NOT_READY .B if command fails with [sk,asc,ascq: 0x2,*,*] .br .I MPATH_PR_SENSE_MEDIUM_ERROR .B if command fails with [sk,asc,ascq: 0x3,*,*] .br .I MPATH_PR_SENSE_HARDWARE_ERROR .B if command fails with [sk,asc,ascq: 0x4,*,*] .br .I MPATH_PR_SENSE_INVALID_OP .B if command fails with [sk,asc,ascq: 0x5,0x20,0x0] .br .I MPATH_PR_ILLEGAL_REQ .B if command fails with [sk,asc,ascq: 0x5,*,*] .br .I MPATH_PR_SENSE_UNIT_ATTENTION .B if command fails with [sk,asc,ascq: 0x6,*,*] .br .I MPATH_PR_SENSE_ABORTED_COMMAND .B if command fails with [sk,asc,ascq: 0xb,*,*] .br .I MPATH_PR_NO_SENSE .B if command fails with [sk,asc,ascq: 0x0,*,*] .br .I MPATH_PR_SENSE_MALFORMED .B if command fails with SCSI command malformed .br .I MPATH_PR_FILE_ERROR .B if command fails while accessing file (device node) problems(e.g. not found) .br .I MPATH_PR_DMMP_ERROR .B if Device Mapper related error.(e.g Error in getting dm info) .br .I MPATH_PR_OTHER .B if other error/warning has occurred(e.g transport or driver error) .br .SH "SEE ALSO" .I mpath_persistent_reserve_out mpathpersist /usr/share/doc/mpathpersist/README .br multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_persistent_reserve_out.3000066400000000000000000000052521260771327300300660ustar00rootroot00000000000000.\" .TH MPATH_PERSISTENT_RESERVE_OUT 3 2011-04-08 "Linux Manpage" .SH NAME mpath_persistent_reserve_out .SH SYNOPSIS .B #include .sp .BI "int mpath_persistent_reserve_out (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose)" .sp .SH DESCRIPTION The function in the .BR mpath_persistent_reserve_out () sends PR OUT command to the DM device and gets the response. .br .BI Parameters: .br .I fd .B The file descriptor of a multipath device. Input argument. .br .I rq_servact .B PROUT command service action. Input argument .br .I rq_scope .B Persistent reservation scope. The value should be always LU_SCOPE (0h). .br .I rq_type .B Persistent reservation type. The valid values of persistent reservation types are 5h (Write exclusive - registrants only) 6h (Exclusive access - registrants only) 7h (Write exclusive - All registrants) 8h (Exclusive access - All registrants). .br .I paramp .B PROUT command parameter data. The paramp is a struct which describes PROUT parameter list. Caller should manage the memory allocation of this structure. .br .I noisy .B Turn on debugging trace: Input argument. 0->Disable, 1->Enable. .br .I verbose .B Set verbosity level. Input argument. value: 0 to 3. 0->Crits and Errors, 1->Warnings, 2->Info, 3->Debug .SH "RETURNS" .I MPATH_PR_SUCCESS .B if PR command successful else returns any one of the status mentioned below .br .I MPATH_PR_SYNTAX_ERROR .B if syntax error or invalid parameter .br .I MPATH_PR_SENSE_NOT_READY .B if command fails with [sk,asc,ascq: 0x2,*,*] .br .I MPATH_PR_SENSE_MEDIUM_ERROR .B if command fails with [sk,asc,ascq: 0x3,*,*] .br .I MPATH_PR_SENSE_HARDWARE_ERROR .B if command fails with [sk,asc,ascq: 0x4,*,*] .br .I MPATH_PR_SENSE_INVALID_OP .B if command fails with [sk,asc,ascq: 0x5,0x20,0x0] .br .I MPATH_PR_ILLEGAL_REQ .B if command fails with [sk,asc,ascq: 0x5,*,*] .br .I MPATH_PR_SENSE_UNIT_ATTENTION .B if command fails with [sk,asc,ascq: 0x6,*,*] .br .I MPATH_PR_SENSE_ABORTED_COMMAND .B if command fails with [sk,asc,ascq: 0xb,*,*] .br .I MPATH_PR_NO_SENSE .B if command fails with [sk,asc,ascq: 0x0,*,*] .br .I MPATH_PR_SENSE_MALFORMED .B if command fails with SCSI command malformed .br .I MPATH_PR_RESERV_CONFLICT .B if command fails with reservation conflict .br .I MPATH_PR_FILE_ERROR .B if command fails while accessing file (device node) problems(e.g. not found) .br .I MPATH_PR_DMMP_ERROR .B if Device Mapper related error.(e.g Error in getting dm info) .br .I MPATH_PR_OTHER .B if other error/warning has occurred(e.g transport or driver error) .br .SH "SEE ALSO" .I mpath_persistent_reserve_in mpathpersist /usr/share/doc/mpathpersist/README .br multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_pr_ioctl.c000066400000000000000000000357331260771327300251460ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "mpath_pr_ioctl.h" #include #include #define FILE_NAME_SIZE 256 #define TIMEOUT 2000 #define MAXRETRY 5 int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy); void mpath_format_readkeys(struct prin_resp *pr_buff, int len , int noisy); void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy); int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t Sensedata, int noisy); void dumpHex(const char* str, int len, int no_ascii); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); uint32_t format_transportids(struct prout_param_descriptor *paramp); void mpath_reverse_uint32_byteorder(uint32_t *num); void mpath_reverse_uint16_byteorder(uint16_t *num); void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length); int get_prin_length(int rq_servact); int mpath_isLittleEndian(void); extern unsigned int mpath_mx_alloc_len; int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) { int status, paramlen = 24, ret = 0; uint32_t translen=0; int retry = MAXRETRY; SenseData_t Sensedata; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; int fd = -1; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_WRONLY); if(fd < 0){ condlog (1, "%s: unable to open device.", dev); return MPATH_PR_FILE_ERROR; } unsigned char cdb[MPATH_PROUT_CMDLEN] = {MPATH_PROUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK) { translen = format_transportids(paramp); paramlen = 24 + translen; } else paramlen = 24; if ( rq_servact > 0) cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); cdb[7] = (unsigned char)((paramlen >> 8) & 0xff); 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); if (noisy) { condlog(3, "%s: Persistent Reservation OUT parameter:", dev); dumpHex((const char *)paramp, paramlen,1); } memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PROUT_CMDLEN; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.timeout = TIMEOUT; if (paramlen > 0) { io_hdr.dxferp = (void *)paramp; io_hdr.dxfer_len = paramlen; io_hdr.dxfer_direction = SG_DXFER_TO_DEV ; } else { io_hdr.dxfer_direction = SG_DXFER_NONE; } ret = ioctl(fd, SG_IO, &io_hdr); if (ret < 0) { condlog(0, "%s: ioctl failed %d", dev, ret); close(fd); return ret; } condlog(2, "%s: Duration=%u (ms)", dev, io_hdr.duration); status = mpath_translate_response(dev, io_hdr, Sensedata, noisy); condlog(3, "%s: status = %d", dev, status); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; condlog(2, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(2, "%s: retrying for sense 02/04/07." " Remaining retries = %d", dev, retry); goto retry; } close(fd); return status; } uint32_t format_transportids(struct prout_param_descriptor *paramp) { int i = 0, len; uint32_t buff_offset = 4; memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN); for (i=0; i < paramp->num_transportid; i++ ) { paramp->private_buffer[buff_offset] = (uint8_t)((paramp->trnptid_list[i]->format_code & 0xff)| (paramp->trnptid_list[i]->protocol_id & 0xff)); buff_offset += 1; switch(paramp->trnptid_list[i]->protocol_id) { case MPATH_PROTOCOL_ID_FC: buff_offset += 7; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->n_port_name, 8); buff_offset +=8 ; buff_offset +=8 ; break; case MPATH_PROTOCOL_ID_SAS: buff_offset += 3; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->sas_address, 8); buff_offset += 12; break; case MPATH_PROTOCOL_ID_ISCSI: buff_offset += 1; len = (paramp->trnptid_list[i]->iscsi_name[1] & 0xff)+2; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->iscsi_name,len); buff_offset += len ; break; } } buff_offset -= 4; paramp->private_buffer[0] = (unsigned char)((buff_offset >> 24) & 0xff); paramp->private_buffer[1] = (unsigned char)((buff_offset >> 16) & 0xff); paramp->private_buffer[2] = (unsigned char)((buff_offset >> 8) & 0xff); paramp->private_buffer[3] = (unsigned char)(buff_offset & 0xff); buff_offset += 4; return buff_offset; } void mpath_format_readkeys( struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); } void mpath_format_readresv(struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); return; } void mpath_format_reportcapabilities(struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.length); mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask); return; } void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy) { int num, k, tid_len_len=0; uint32_t fdesc_count=0; unsigned char *p; char *ppbuff; uint32_t additional_length; mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor); if (0 == pr_buff->prin_descriptor.prin_readfd.number_of_descriptor) { return ; } if (pr_buff->prin_descriptor.prin_readfd.number_of_descriptor == 0) { condlog(2, "No registration or resrvation found."); return; } additional_length = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; char tempbuff[MPATH_MAX_PARAM_LEN]; struct prin_fulldescr fdesc; memset(&fdesc, 0, sizeof(struct prin_fulldescr)); memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,MPATH_MAX_PARAM_LEN ); memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0, MPATH_MAX_PARAM_LEN); p =(unsigned char *)tempbuff; ppbuff = (char *)pr_buff->prin_descriptor.prin_readfd.private_buffer; for (k = 0; k < additional_length; k += num, p += num) { memcpy(&fdesc.key, p, 8 ); fdesc.flag = p[12]; fdesc.scope_type = p[13]; fdesc.rtpi = ((p[18] << 8) | p[19]); tid_len_len = ((p[20] << 24) | (p[21] << 16) | (p[22] << 8) | p[23]); if (tid_len_len > 0) decode_transport_id( &fdesc, &p[24], tid_len_len); num = 24 + tid_len_len; memcpy(ppbuff, &fdesc, sizeof(struct prin_fulldescr)); pr_buff->prin_descriptor.prin_readfd.descriptors[fdesc_count]= (struct prin_fulldescr *)ppbuff; ppbuff += sizeof(struct prin_fulldescr); ++fdesc_count; } pr_buff->prin_descriptor.prin_readfd.number_of_descriptor = fdesc_count; return; } void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length) { int num, k; int jump; for (k = 0, jump = 24; k < length; k += jump, p += jump) { fdesc->trnptid.format_code = ((p[0] >> 6) & 0x3); fdesc->trnptid.protocol_id = (p[0] & 0xf); switch (fdesc->trnptid.protocol_id) { case MPATH_PROTOCOL_ID_FC: memcpy(&fdesc->trnptid.n_port_name, &p[8], 8); jump = 24; break; case MPATH_PROTOCOL_ID_ISCSI: num = ((p[2] << 8) | p[3]); memcpy(&fdesc->trnptid.iscsi_name, &p[4], num); jump = (((num + 4) < 24) ? 24 : num + 4); break; case MPATH_PROTOCOL_ID_SAS: memcpy(&fdesc->trnptid.sas_address, &p[4], 8); jump = 24; break; default: jump = 24; break; } } } int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy) { int ret, status, got, fd; int mx_resp_len; SenseData_t Sensedata; int retry = MAXRETRY; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; unsigned char cdb[MPATH_PRIN_CMDLEN] = {MPATH_PRIN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_WRONLY); if(fd < 0){ condlog(0, "%s: Unable to open device ", dev); return MPATH_PR_FILE_ERROR; } if (mpath_mx_alloc_len) mx_resp_len = mpath_mx_alloc_len; else mx_resp_len = get_prin_length(rq_servact); if (mx_resp_len == 0) { status = MPATH_PR_SYNTAX_ERROR; goto out; } cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); cdb[8] = (unsigned char)(mx_resp_len & 0xff); retry : memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PRIN_CMDLEN; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.timeout = TIMEOUT; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = (void *)resp; ret =ioctl(fd, SG_IO, &io_hdr); if (ret < 0){ condlog(0, "%s: IOCTL failed %d", dev, ret); status = MPATH_PR_OTHER; goto out; } 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); 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); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(2, "%s: retrying for 02/04/07. Remaining retries = %d", dev, retry); goto retry; } if (status != MPATH_PR_SUCCESS) goto out; if (noisy) dumpHex((const char *)resp, got , 1); switch (rq_servact) { case MPATH_PRIN_RKEY_SA : mpath_format_readkeys(resp, got, noisy); break; case MPATH_PRIN_RRES_SA : mpath_format_readresv(resp, got, noisy); break; case MPATH_PRIN_RCAP_SA : mpath_format_reportcapabilities(resp, got, noisy); break; case MPATH_PRIN_RFSTAT_SA : mpath_format_readfullstatus(resp, got, noisy); } out: close(fd); return status; } int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t Sensedata, int noisy) { condlog(3, "%s: status driver:%02x host:%02x scsi:%02x", dev, io_hdr.driver_status, io_hdr.host_status ,io_hdr.status); io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) { return MPATH_PR_SUCCESS; } switch(io_hdr.status) { case SAM_STAT_GOOD: break; case SAM_STAT_CHECK_CONDITION: condlog(2, "%s: Sense_Key=%02x, ASC=%02x ASCQ=%02x", dev, Sensedata.Sense_Key, Sensedata.ASC, Sensedata.ASCQ); switch(Sensedata.Sense_Key) { case NO_SENSE: return MPATH_PR_NO_SENSE; case RECOVERED_ERROR: return MPATH_PR_SUCCESS; case NOT_READY: return MPATH_PR_SENSE_NOT_READY; case MEDIUM_ERROR: return MPATH_PR_SENSE_MEDIUM_ERROR; case BLANK_CHECK: return MPATH_PR_OTHER; case HARDWARE_ERROR: return MPATH_PR_SENSE_HARDWARE_ERROR; case ILLEGAL_REQUEST: return MPATH_PR_ILLEGAL_REQ; case UNIT_ATTENTION: return MPATH_PR_SENSE_UNIT_ATTENTION; case DATA_PROTECT: case COPY_ABORTED: return MPATH_PR_OTHER; case ABORTED_COMMAND: return MPATH_PR_SENSE_ABORTED_COMMAND; default : return MPATH_PR_OTHER; } case SAM_STAT_RESERVATION_CONFLICT: return MPATH_PR_RESERV_CONFLICT; default : return MPATH_PR_OTHER; } switch(io_hdr.host_status) { case DID_OK : break; default : return MPATH_PR_OTHER; } switch(io_hdr.driver_status) { case DRIVER_OK: break; default : return MPATH_PR_OTHER; } return MPATH_PR_SUCCESS; } int mpath_isLittleEndian() { int num = 1; if(*(char *)&num == 1) { condlog(2, "Little-Endian"); } else { condlog(2, "Big-Endian"); } return 0; } void mpath_reverse_uint16_byteorder(uint16_t *num) { uint16_t byte0, byte1; byte0 = (*num & 0x000000FF) >> 0 ; byte1 = (*num & 0x0000FF00) >> 8 ; *num = ((byte0 << 8) | (byte1 << 0)); } void mpath_reverse_uint32_byteorder(uint32_t *num) { uint32_t byte0, byte1, byte2, byte3; byte0 = (*num & 0x000000FF) >> 0 ; byte1 = (*num & 0x0000FF00) >> 8 ; byte2 = (*num & 0x00FF0000) >> 16 ; byte3 = (*num & 0xFF000000) >> 24 ; *num = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0)); } void mpath_reverse_8bytes_order(char * var) { char byte[8]; int i; for(i=0 ; i < 8 ; i++ ) { byte[i] = var[i]; } for(i=0 ; i < 8 ; i++ ) { var[7 - i] = byte[i]; } } void dumpHex(const char* str, int len, int log) { const char * p = str; unsigned char c; char buff[82]; const int bpstart = 5; int bpos = bpstart; int k; if (len <= 0) return; memset(buff, ' ', 80); buff[80] = '\0'; for (k = 0; k < len; k++) { c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { if (log) condlog(0, "%.76s" , buff); else printf("%.76s" , buff); bpos = bpstart; memset(buff, ' ', 80); } } if (bpos > bpstart) { buff[bpos + 2] = '\0'; if (log) condlog(0, "%s", buff); else printf("%s\n" , buff); } return; } int get_prin_length(int rq_servact) { int mx_resp_len; switch (rq_servact) { case MPATH_PRIN_RKEY_SA: mx_resp_len = sizeof(struct prin_readdescr); break; case MPATH_PRIN_RRES_SA : mx_resp_len = sizeof(struct prin_resvdescr); break; case MPATH_PRIN_RCAP_SA : mx_resp_len = sizeof(struct prin_capdescr); break; case MPATH_PRIN_RFSTAT_SA: mx_resp_len = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*32; break; default: condlog(0, "invalid service action, %d", rq_servact); mx_resp_len = 0; break; } return mx_resp_len; } multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_pr_ioctl.h000066400000000000000000000065361260771327300251520ustar00rootroot00000000000000#define MPATH_XFER_HOST_DEV 0 /*data transfer from initiator to target */ #define MPATH_XFER_DEV_HOST 1 /*data transfer from target to initiator */ #define MPATH_XFER_NONE 2 /*no data transfer */ #define MPATH_XFER_UNKNOWN 3 /*data transfer direction is unknown */ #if 0 static const char * pr_type_strs[] = { "obsolete [0]", "Write Exclusive", "obsolete [2]", "Exclusive Access", "obsolete [4]", "Write Exclusive, registrants only", "Exclusive Access, registrants only", "Write Exclusive, all registrants", "Exclusive Access, all registrants", "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]", "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]", }; #endif typedef unsigned int LWORD; /* unsigned numeric, bit patterns */ typedef unsigned char BYTE; /* unsigned numeric, bit patterns */ typedef struct SenseData { BYTE Error_Code; BYTE Segment_Number; /* not applicable to DAC */ BYTE Sense_Key; BYTE Information[ 4 ]; BYTE Additional_Len; LWORD Command_Specific_Info; BYTE ASC; BYTE ASCQ; BYTE Field_Replaceable_Unit; BYTE Sense_Key_Specific_Info[ 3 ]; BYTE Recovery_Action[ 2 ]; BYTE Total_Errors; BYTE Total_Retries; BYTE ASC_Stack_1; BYTE ASCQ_Stack_1; BYTE ASC_Stack_2; BYTE ASCQ_Stack_2; BYTE Additional_FRU_Info[ 8 ]; BYTE Error_Specific_Info[ 3 ]; BYTE Error_Detection_Point[ 4 ]; BYTE Original_CDB[10]; BYTE Host_ID; BYTE Host_Descriptor[ 2 ]; BYTE Serial_Number[ 16 ]; BYTE Array_SW_Revision[ 4 ]; BYTE Data_Xfer_Operation; BYTE LUN_Number; BYTE LUN_Status; BYTE Drive_ID; BYTE Xfer_Start_Drive_ID; BYTE Drive_SW_Revision[ 4 ]; BYTE Drive_Product_ID[ 16 ]; BYTE PowerUp_Status[ 2 ]; BYTE RAID_Level; BYTE Drive_Sense_ID[ 2 ]; BYTE Drive_Sense_Data[ 32 ]; BYTE Reserved2[24]; } SenseData_t; #define MPATH_PRIN_CMD 0x5e #define MPATH_PRIN_CMDLEN 10 #define MPATH_PROUT_CMD 0x5f #define MPATH_PROUT_CMDLEN 10 #define DID_OK 0x00 /* * Status codes */ #define SAM_STAT_GOOD 0x00 #define SAM_STAT_CHECK_CONDITION 0x02 #define SAM_STAT_CONDITION_MET 0x04 #define SAM_STAT_BUSY 0x08 #define SAM_STAT_INTERMEDIATE 0x10 #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 #define SAM_STAT_RESERVATION_CONFLICT 0x18 #define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ #define SAM_STAT_TASK_SET_FULL 0x28 #define SAM_STAT_ACA_ACTIVE 0x30 #define SAM_STAT_TASK_ABORTED 0x40 #define STATUS_MASK 0x3e /* * SENSE KEYS */ #define NO_SENSE 0x00 #define RECOVERED_ERROR 0x01 #define NOT_READY 0x02 #define MEDIUM_ERROR 0x03 #define HARDWARE_ERROR 0x04 #define ILLEGAL_REQUEST 0x05 #define UNIT_ATTENTION 0x06 #define DATA_PROTECT 0x07 #define BLANK_CHECK 0x08 #define COPY_ABORTED 0x0a #define ABORTED_COMMAND 0x0b #define VOLUME_OVERFLOW 0x0d #define MISCOMPARE 0x0e /* Driver status */ #define DRIVER_OK 0x00 multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpath_updatepr.c000066400000000000000000000022711260771327300251460ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "memory.h" #include "../libmultipath/uxsock.h" #include "../libmultipath/defaults.h" unsigned long mem_allocated; /* Total memory used in Bytes */ int update_prflag(char * arg1, char * arg2, int noisy) { int fd; char str[64]; char *reply; size_t len; int ret = 0; fd = ux_socket_connect(DEFAULT_SOCKET); if (fd == -1) { condlog (0, "ux socket connect error"); return 1 ; } snprintf(str,sizeof(str),"map %s %s", arg1, arg2); condlog (2, "%s: pr flag message=%s", arg1, str); send_packet(fd, str, strlen(str) + 1); ret = recv_packet(fd, &reply, &len, DEFAULT_UXSOCK_TIMEOUT); if (ret < 0) { condlog(2, "%s: message=%s error=%d", arg1, str, -ret); ret = -2; } else { condlog (2, "%s: message=%s reply=%s", arg1, str, reply); if (!reply || strncmp(reply,"ok", 2) == 0) ret = -1; else if (strncmp(reply, "fail", 4) == 0) ret = -2; else{ ret = atoi(reply); } } free(reply); return ret; } multipath-tools-0.5.0+git1.656f8865/libmpathpersist/mpathpr.h000066400000000000000000000034451260771327300236150ustar00rootroot00000000000000#ifndef MPATHPR_H #define MPATHPR_H struct prin_param { char dev[FILE_NAME_SIZE]; int rq_servact; struct prin_resp *resp; int noisy; int status; }; struct prout_param { char dev[FILE_NAME_SIZE]; int rq_servact; int rq_scope; unsigned int rq_type; struct prout_param_descriptor *paramp; int noisy; int status; }; struct threadinfo { int status; pthread_t id; struct prout_param param; }; struct config * conf; int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); void * _mpath_pr_update (void *arg); int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy); int get_mpvec (vector curmp, vector pathvec, char * refwwid); void * mpath_prout_pthread_fn(void *p); void dumpHex(const char* , int len, int no_ascii); int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int send_prout_activepath(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int update_prflag(char * arg1, char * arg2, int noisy); void * mpath_alloc_prin_response(int prin_sa); int update_map_pr(struct multipath *mpp); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/000077500000000000000000000000001260771327300212475ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/libmultipath/Makefile000066400000000000000000000037211260771327300227120ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc SONAME=0 DEVLIB = libmultipath.so LIBS = $(DEVLIB).$(SONAME) LIBDEPS = -lpthread -ldl -ldevmapper -ludev ifdef SYSTEMD ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) LIBDEPS += -lsystemd else LIBDEPS += -lsystemd-daemon endif endif OBJS = memory.o parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ structs.o discovery.o propsel.o dict.o \ pgpolicies.o debug.o defaults.o uevent.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 LIBDM_API_FLUSH = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_no_flush' /usr/include/libdevmapper.h) ifneq ($(strip $(LIBDM_API_FLUSH)),0) CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE endif LIBDM_API_COOKIE = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_set_cookie' /usr/include/libdevmapper.h) ifneq ($(strip $(LIBDM_API_COOKIE)),0) CFLAGS += -DLIBDM_API_COOKIE endif LIBUDEV_API_RECVBUF = $(shell grep -Ecs '^[a-z]*[[:space:]]+udev_monitor_set_resolve_buffer_size' /usr/include/libudev.h) ifneq ($(strip $(LIBUDEV_API_RECVBUF)),0) CFLAGS += -DLIBUDEV_API_RECVBUF endif ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) endif LIBDM_API_DEFERRED = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_deferred_remove' /usr/include/libdevmapper.h) ifneq ($(strip $(LIBDM_API_DEFERRED)),0) CFLAGS += -DLIBDM_API_DEFERRED endif all: $(LIBS) $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS) ln -sf $@ $(DEVLIB) install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(libdir) uninstall: rm -f $(DESTDIR)$(syslibdir)/$(LIBS) clean: rm -f core *.a *.o *.gz *.so *.so.* multipath-tools-0.5.0+git1.656f8865/libmultipath/alias.c000066400000000000000000000175341260771327300225160ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include "debug.h" #include "uxsock.h" #include "alias.h" #include "file.h" #include "vector.h" #include "checkers.h" #include "structs.h" /* * significant parts of this file were taken from iscsi-bindings.c of the * linux-iscsi project. * Copyright (C) 2002 Cisco Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 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. * * See the file COPYING included with this distribution for more details. */ static int format_devname(char *name, int id, int len, char *prefix) { int pos; int prefix_len = strlen(prefix); memset(name,0, len); strcpy(name, prefix); for (pos = len - 1; pos >= prefix_len; pos--) { id--; name[pos] = 'a' + id % 26; if (id < 26) break; id /= 26; } memmove(name + prefix_len, name + pos, len - pos); name[prefix_len + len - pos] = '\0'; return (prefix_len + len - pos); } static int scan_devname(char *alias, char *prefix) { char *c; int i, n = 0; if (!prefix || strncmp(alias, prefix, strlen(prefix))) return -1; if (strlen(alias) == strlen(prefix)) return -1; if (strlen(alias) > strlen(prefix) + 7) /* id of 'aaaaaaaa' overflows int */ return -1; c = alias + strlen(prefix); while (*c != '\0' && *c != ' ' && *c != '\t') { if (*c < 'a' || *c > 'z') return -1; i = *c - 'a'; n = ( n * 26 ) + i; if (n < 0) return -1; c++; n++; } return n; } static int lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix) { char buf[LINE_MAX]; unsigned int line_nr = 0; int id = 1; int biggest_id = 1; int smallest_bigger_id = INT_MAX; *map_alias = NULL; while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; line_nr++; c = strpbrk(buf, "#\n\r"); if (c) *c = '\0'; alias = strtok(buf, " \t"); if (!alias) /* blank line */ continue; curr_id = scan_devname(alias, prefix); if (curr_id == id) id++; if (curr_id > biggest_id) biggest_id = curr_id; if (curr_id > id && curr_id < smallest_bigger_id) smallest_bigger_id = curr_id; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, "Ignoring malformed line %u in bindings file", line_nr); continue; } if (strcmp(wwid, map_wwid) == 0){ condlog(3, "Found matching wwid [%s] in bindings file." " Setting alias to %s", wwid, alias); *map_alias = strdup(alias); if (*map_alias == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); return 0; } } condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); if (id < 0) { condlog(0, "no more available user_friendly_names"); return 0; } if (id < smallest_bigger_id) return id; return biggest_id + 1; } static int rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix) { char line[LINE_MAX]; unsigned int line_nr = 0; buff[0] = '\0'; while (fgets(line, LINE_MAX, f)) { char *c, *alias, *wwid; line_nr++; c = strpbrk(line, "#\n\r"); if (c) *c = '\0'; alias = strtok(line, " \t"); if (!alias) /* blank line */ continue; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, "Ignoring malformed line %u in bindings file", line_nr); continue; } if (strlen(wwid) > WWID_SIZE - 1) { condlog(3, "Ignoring too large wwid at %u in bindings file", line_nr); continue; } if (strcmp(alias, map_alias) == 0){ condlog(3, "Found matching alias [%s] in bindings file." "\nSetting wwid to %s", alias, wwid); strncpy(buff, wwid, WWID_SIZE); buff[WWID_SIZE - 1] = '\0'; return 0; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); return -1; } static char * allocate_binding(int fd, char *wwid, int id, char *prefix) { char buf[LINE_MAX]; off_t offset; char *alias, *c; int i; if (id < 0) { condlog(0, "Bindings file full. Cannot allocate new binding"); return NULL; } i = format_devname(buf, id, LINE_MAX, prefix); c = buf + i; snprintf(c,LINE_MAX - i, " %s\n", wwid); buf[LINE_MAX - 1] = '\0'; offset = lseek(fd, 0, SEEK_END); if (offset < 0){ condlog(0, "Cannot seek to end of bindings file : %s", strerror(errno)); return NULL; } if (write_all(fd, buf, strlen(buf)) != strlen(buf)){ condlog(0, "Cannot write binding to bindings file : %s", strerror(errno)); /* clear partial write */ if (ftruncate(fd, offset)) condlog(0, "Cannot truncate the header : %s", strerror(errno)); return NULL; } c = strchr(buf, ' '); *c = '\0'; alias = strdup(buf); if (alias == NULL) condlog(0, "cannot copy new alias from bindings file : %s", strerror(errno)); else condlog(3, "Created new binding [%s] for WWID [%s]", alias, wwid); return alias; } char * use_existing_alias (char *wwid, char *file, char *alias_old, char *prefix, int bindings_read_only) { char *alias = NULL; int id = 0; int fd, can_write; char buff[WWID_SIZE]; FILE *f; fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor"); close(fd); return NULL; } /* lookup the binding. if it exsists, the wwid will be in buff * either way, id contains the id for the alias */ rlookup_binding(f, buff, alias_old, prefix); if (strlen(buff) > 0) { /* if buff is our wwid, it's already * allocated correctly */ if (strcmp(buff, wwid) == 0) alias = STRDUP(alias_old); else { alias = NULL; condlog(0, "alias %s already bound to wwid %s, cannot reuse", alias_old, buff); } goto out; } /* allocate the existing alias in the bindings file */ id = scan_devname(alias_old, prefix); if (id <= 0) goto out; if (fflush(f) != 0) { condlog(0, "cannot fflush bindings file stream : %s", strerror(errno)); goto out; } if (can_write && !bindings_read_only) { alias = allocate_binding(fd, wwid, id, prefix); condlog(0, "Allocated existing binding [%s] for WWID [%s]", alias, wwid); } out: fclose(f); return alias; } char * get_user_friendly_alias(char *wwid, char *file, char *prefix, int bindings_read_only) { char *alias; int fd, id; FILE *f; int can_write; if (!wwid || *wwid == '\0') { condlog(3, "Cannot find binding for empty WWID"); return NULL; } fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(fd); return NULL; } id = lookup_binding(f, wwid, &alias, prefix); if (id < 0) { fclose(f); return NULL; } if (fflush(f) != 0) { condlog(0, "cannot fflush bindings file stream : %s", strerror(errno)); free(alias); fclose(f); return NULL; } if (!alias && can_write && !bindings_read_only && id) alias = allocate_binding(fd, wwid, id, prefix); fclose(f); return alias; } int get_user_friendly_wwid(char *alias, char *buff, char *file) { int fd, unused; FILE *f; if (!alias || *alias == '\0') { condlog(3, "Cannot find binding for empty alias"); return -1; } fd = open_file(file, &unused, BINDINGS_FILE_HEADER); if (fd < 0) return -1; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(fd); return -1; } rlookup_binding(f, buff, alias, NULL); if (!strlen(buff)) { fclose(f); return -1; } fclose(f); return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/alias.h000066400000000000000000000010421260771327300225060ustar00rootroot00000000000000#define BINDINGS_FILE_HEADER \ "# Multipath bindings, Version : 1.0\n" \ "# NOTE: this file is automatically maintained by the multipath program.\n" \ "# You should not need to edit this file in normal circumstances.\n" \ "#\n" \ "# Format:\n" \ "# alias wwid\n" \ "#\n" char *get_user_friendly_alias(char *wwid, char *file, char *prefix, int bindings_readonly); int get_user_friendly_wwid(char *alias, char *buff, char *file); char *use_existing_alias (char *wwid, char *file, char *alias_old, char *prefix, int bindings_read_only); multipath-tools-0.5.0+git1.656f8865/libmultipath/blacklist.c000066400000000000000000000214231260771327300233650ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "util.h" #include "debug.h" #include "structs.h" #include "config.h" #include "blacklist.h" extern int store_ble (vector blist, char * str, int origin) { struct blentry * ble; if (!str) return 0; if (!blist) goto out; ble = MALLOC(sizeof(struct blentry)); if (!ble) goto out; if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB)) goto out1; if (!vector_alloc_slot(blist)) goto out1; ble->str = str; ble->origin = origin; vector_set_slot(blist, ble); return 0; out1: FREE(ble); out: FREE(str); return 1; } extern int alloc_ble_device (vector blist) { struct blentry_device * ble = MALLOC(sizeof(struct blentry_device)); if (!ble) return 1; if (!blist || !vector_alloc_slot(blist)) { FREE(ble); return 1; } vector_set_slot(blist, ble); return 0; } extern int set_ble_device (vector blist, char * vendor, char * product, int origin) { struct blentry_device * ble; if (!blist) return 1; ble = VECTOR_LAST_SLOT(blist); if (!ble) return 1; if (vendor) { if (regcomp(&ble->vendor_reg, vendor, REG_EXTENDED|REG_NOSUB)) { FREE(vendor); if (product) FREE(product); return 1; } ble->vendor = vendor; } if (product) { if (regcomp(&ble->product_reg, product, REG_EXTENDED|REG_NOSUB)) { FREE(product); if (vendor) { ble->vendor = NULL; FREE(vendor); } return 1; } ble->product = product; } ble->origin = origin; return 0; } int _blacklist_exceptions (vector elist, const char * str) { int i; struct blentry * ele; vector_foreach_slot (elist, ele, i) { if (!regexec(&ele->regex, str, 0, NULL, 0)) return 1; } return 0; } int _blacklist (vector blist, const char * str) { int i; struct blentry * ble; vector_foreach_slot (blist, ble, i) { if (!regexec(&ble->regex, str, 0, NULL, 0)) return 1; } return 0; } int _blacklist_exceptions_device(vector elist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (elist, ble, i) { if (!ble->vendor && !ble->product) continue; if ((!ble->vendor || !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && (!ble->product || !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; } int _blacklist_device (vector blist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (blist, ble, i) { if (!ble->vendor && !ble->product) continue; if ((!ble->vendor || !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && (!ble->product || !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; } int setup_default_blist (struct config * conf) { struct blentry * ble; struct hwentry *hwe; char * str; int i; str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^(td|hd|vd)[a-z]"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^dcssblk[0-9]*"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^nvme.*"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("(SCSI_IDENT_.*|ID_WWN)"); if (!str) return 1; if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT)) return 1; vector_foreach_slot (conf->hwtable, hwe, i) { if (hwe->bl_product) { if (_blacklist_device(conf->blist_device, hwe->vendor, hwe->bl_product)) continue; if (alloc_ble_device(conf->blist_device)) return 1; ble = VECTOR_SLOT(conf->blist_device, VECTOR_SIZE(conf->blist_device) -1); if (set_ble_device(conf->blist_device, STRDUP(hwe->vendor), STRDUP(hwe->bl_product), ORIGIN_DEFAULT)) { FREE(ble); vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1); return 1; } } } return 0; } #define LOG_BLIST(M,S) \ if (vendor && product) \ condlog(3, "%s: (%s:%s) %s %s", \ dev, vendor, product, (M), (S)); \ else if (wwid && !dev) \ condlog(3, "%s: %s %s", wwid, (M), (S)); \ else if (wwid) \ condlog(3, "%s: %s %s %s", dev, (M), wwid, (S)); \ else if (env) \ condlog(3, "%s: %s %s %s", dev, (M), env, (S)); \ else \ condlog(3, "%s: %s %s", dev, (M), (S)) void log_filter (const char *dev, char *vendor, char *product, char *wwid, const char *env, int r) { /* * Try to sort from most likely to least. */ switch (r) { case MATCH_NOTHING: break; case MATCH_DEVICE_BLIST: LOG_BLIST("vendor/product", "blacklisted"); break; case MATCH_WWID_BLIST: LOG_BLIST("wwid", "blacklisted"); break; case MATCH_DEVNODE_BLIST: LOG_BLIST("device node name", "blacklisted"); break; case MATCH_PROPERTY_BLIST: LOG_BLIST("udev property", "blacklisted"); break; case MATCH_DEVICE_BLIST_EXCEPT: LOG_BLIST("vendor/product", "whitelisted"); break; case MATCH_WWID_BLIST_EXCEPT: LOG_BLIST("wwid", "whitelisted"); break; case MATCH_DEVNODE_BLIST_EXCEPT: LOG_BLIST("device node name", "whitelisted"); break; case MATCH_PROPERTY_BLIST_EXCEPT: LOG_BLIST("udev property", "whitelisted"); break; case MATCH_PROPERTY_BLIST_MISSING: LOG_BLIST("blacklisted,", "udev property missing"); break; } } int _filter_device (vector blist, vector elist, char * vendor, char * product) { if (!vendor || !product) return 0; if (_blacklist_exceptions_device(elist, vendor, product)) return MATCH_DEVICE_BLIST_EXCEPT; if (_blacklist_device(blist, vendor, product)) return MATCH_DEVICE_BLIST; return 0; } int filter_device (vector blist, vector elist, char * vendor, char * product) { int r = _filter_device(blist, elist, vendor, product); log_filter(NULL, vendor, product, NULL, NULL, r); return r; } int _filter_devnode (vector blist, vector elist, char * dev) { if (!dev) return 0; if (_blacklist_exceptions(elist, dev)) return MATCH_DEVNODE_BLIST_EXCEPT; if (_blacklist(blist, dev)) return MATCH_DEVNODE_BLIST; return 0; } int filter_devnode (vector blist, vector elist, char * dev) { int r = _filter_devnode(blist, elist, dev); log_filter(dev, NULL, NULL, NULL, NULL, r); return r; } int _filter_wwid (vector blist, vector elist, char * wwid) { if (!wwid) return 0; if (_blacklist_exceptions(elist, wwid)) return MATCH_WWID_BLIST_EXCEPT; if (_blacklist(blist, wwid)) return MATCH_WWID_BLIST; return 0; } int filter_wwid (vector blist, vector elist, char * wwid, char * dev) { int r = _filter_wwid(blist, elist, wwid); log_filter(dev, NULL, NULL, wwid, NULL, r); return r; } int _filter_path (struct config * conf, struct path * pp) { int r; r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); if (r > 0) return r; r = _filter_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id); if (r > 0) return r; r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid); return r; } int filter_path (struct config * conf, struct path * pp) { int r=_filter_path(conf, pp); log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, NULL, r); return r; } int _filter_property (struct config *conf, const char *env) { if (_blacklist_exceptions(conf->elist_property, env)) return MATCH_PROPERTY_BLIST_EXCEPT; if (_blacklist(conf->blist_property, env)) return MATCH_PROPERTY_BLIST; return 0; } int filter_property(struct config * conf, struct udev_device * udev) { const char *devname = udev_device_get_sysname(udev); struct udev_list_entry *list_entry; int r; if (!udev) return 0; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev)) { const char *env; env = udev_list_entry_get_name(list_entry); if (!env) continue; r = _filter_property(conf, env); if (r) { log_filter(devname, NULL, NULL, NULL, env, r); return r; } } /* * This is the inverse of the 'normal' matching; * the environment variable _has_ to match. */ log_filter(devname, NULL, NULL, NULL, NULL, MATCH_PROPERTY_BLIST_MISSING); return MATCH_PROPERTY_BLIST_MISSING; } void free_blacklist (vector blist) { struct blentry * ble; int i; if (!blist) return; vector_foreach_slot (blist, ble, i) { if (ble) { regfree(&ble->regex); FREE(ble->str); FREE(ble); } } vector_free(blist); } void free_blacklist_device (vector blist) { struct blentry_device * ble; int i; if (!blist) return; vector_foreach_slot (blist, ble, i) { if (ble) { if (ble->vendor) { regfree(&ble->vendor_reg); FREE(ble->vendor); } if (ble->product) { regfree(&ble->product_reg); FREE(ble->product); } FREE(ble); } } vector_free(blist); } multipath-tools-0.5.0+git1.656f8865/libmultipath/blacklist.h000066400000000000000000000022541260771327300233730ustar00rootroot00000000000000#ifndef _BLACKLIST_H #define _BLACKLIST_H #include #include "regex.h" #define MATCH_NOTHING 0 #define MATCH_WWID_BLIST 1 #define MATCH_DEVICE_BLIST 2 #define MATCH_DEVNODE_BLIST 3 #define MATCH_PROPERTY_BLIST 4 #define MATCH_PROPERTY_BLIST_MISSING 5 #define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST #define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST #define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST #define MATCH_PROPERTY_BLIST_EXCEPT -MATCH_PROPERTY_BLIST struct blentry { char * str; regex_t regex; int origin; }; struct blentry_device { char * vendor; char * product; regex_t vendor_reg; regex_t product_reg; int origin; }; int setup_default_blist (struct config *); int alloc_ble_device (vector); int filter_devnode (vector, vector, char *); int filter_wwid (vector, vector, char *, char *); int filter_device (vector, vector, char *, char *); int filter_path (struct config *, struct path *); int filter_property(struct config *, struct udev_device *); int store_ble (vector, char *, int); int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); #endif /* _BLACKLIST_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/callout.c000066400000000000000000000071031260771327300230570ustar00rootroot00000000000000/* * Source: copy of the udev package source file * * Copyrights of the source file apply * Copyright (c) 2004 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "util.h" #include "debug.h" int execute_program(char *path, char *value, int len) { int retval; int count; int status; int fds[2], null_fd; pid_t pid; char *pos; char arg[CALLOUT_MAX_SIZE]; int argc = sizeof(arg) / 2; char *argv[argc + 1]; int i; i = 0; if (strchr(path, ' ')) { strlcpy(arg, path, sizeof(arg)); pos = arg; while (pos != NULL && i < argc) { if (pos[0] == '\'') { /* don't separate if in apostrophes */ pos++; argv[i] = strsep(&pos, "\'"); while (pos[0] == ' ') pos++; } else { argv[i] = strsep(&pos, " "); } i++; } } else { argv[i++] = path; } argv[i] = NULL; retval = pipe(fds); if (retval != 0) { condlog(0, "error creating pipe for callout: %s", strerror(errno)); return -1; } pid = fork(); switch(pid) { case 0: /* child */ close(STDOUT_FILENO); /* dup write side of pipe to STDOUT */ if (dup(fds[1]) < 0) return -1; /* Ignore writes to stderr */ null_fd = open("/dev/null", O_WRONLY); if (null_fd > 0) { close(STDERR_FILENO); retval = dup(null_fd); close(null_fd); } retval = execv(argv[0], argv); condlog(0, "error execing %s : %s", argv[0], strerror(errno)); exit(-1); case -1: condlog(0, "fork failed: %s", strerror(errno)); close(fds[0]); close(fds[1]); return -1; default: /* parent reads from fds[0] */ close(fds[1]); retval = 0; i = 0; while (1) { count = read(fds[0], value + i, len - i-1); if (count <= 0) break; i += count; if (i >= len-1) { condlog(0, "not enough space for response from %s", argv[0]); retval = -1; break; } } if (count < 0) { condlog(0, "no response from %s", argv[0]); retval = -1; } if (i > 0 && value[i-1] == '\n') i--; value[i] = '\0'; wait(&status); close(fds[0]); retval = -1; if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status == 0) retval = 0; else condlog(0, "%s exited with %d", argv[0], status); } else if (WIFSIGNALED(status)) condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status)); else condlog(0, "%s terminated abnormally", argv[0]); } return retval; } extern int apply_format (char * string, char * cmd, struct path * pp) { char * pos; char * dst; char * p; char * q; int len; int myfree; if (!string) return 1; if (!cmd) return 1; dst = cmd; p = dst; pos = strchr(string, '%'); myfree = CALLOUT_MAX_SIZE; if (!pos) { strcpy(dst, string); return 0; } len = (int) (pos - string) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", string); p += len - 1; pos++; switch (*pos) { case 'n': len = strlen(pp->dev) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pp->dev); for (q = p; q < p + len; q++) { if (q && *q == '!') *q = '/'; } p += len - 1; break; case 'd': len = strlen(pp->dev_t) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pp->dev_t); p += len - 1; break; default: break; } pos++; if (!*pos) return 0; len = strlen(pos) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pos); condlog(3, "reformated callout = %s", dst); return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/callout.h000066400000000000000000000002341260771327300230620ustar00rootroot00000000000000#ifndef _CALLOUT_H #define _CALLOUT_H int execute_program(char *, char *, int); int apply_format (char *, char *, struct path *); #endif /* _CALLOUT_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers.c000066400000000000000000000113671260771327300232120ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "checkers.h" #include "vector.h" #include "config.h" char *checker_state_names[] = { "wild", "unchecked", "down", "up", "shaky", "ghost", "pending", "timeout", "removed", "delayed", }; static LIST_HEAD(checkers); char * checker_state_name (int i) { return checker_state_names[i]; } int init_checkers (void) { if (!add_checker(DEFAULT_CHECKER)) return 1; return 0; } struct checker * alloc_checker (void) { struct checker *c; c = MALLOC(sizeof(struct checker)); if (c) { INIT_LIST_HEAD(&c->node); c->refcount = 1; } return c; } void free_checker (struct checker * c) { if (!c) return; c->refcount--; if (c->refcount) { condlog(3, "%s checker refcount %d", c->name, c->refcount); return; } condlog(3, "unloading %s checker", c->name); list_del(&c->node); if (c->handle) { if (dlclose(c->handle) != 0) { condlog(0, "Cannot unload checker %s: %s", c->name, dlerror()); } } FREE(c); } void cleanup_checkers (void) { struct checker * checker_loop; struct checker * checker_temp; list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) { free_checker(checker_loop); } } struct checker * checker_lookup (char * name) { struct checker * c; if (!name || !strlen(name)) return NULL; list_for_each_entry(c, &checkers, node) { if (!strncmp(name, c->name, CHECKER_NAME_LEN)) return c; } return add_checker(name); } struct checker * add_checker (char * name) { char libname[LIB_CHECKER_NAMELEN]; struct stat stbuf; struct checker * c; char *errstr; c = alloc_checker(); if (!c) return NULL; snprintf(c->name, CHECKER_NAME_LEN, "%s", name); snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so", conf->multipath_dir, name); if (stat(libname,&stbuf) < 0) { condlog(0,"Checker '%s' not found in %s", name, conf->multipath_dir); goto out; } condlog(3, "loading %s checker", libname); c->handle = dlopen(libname, RTLD_NOW); if (!c->handle) { if ((errstr = dlerror()) != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); goto out; } c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->check) goto out; c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->init) goto out; c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->free) goto out; c->fd = 0; c->sync = 1; list_add(&c->node, &checkers); return c; out: free_checker(c); return NULL; } void checker_set_fd (struct checker * c, int fd) { if (!c) return; c->fd = fd; } void checker_set_sync (struct checker * c) { if (!c) return; c->sync = 1; } void checker_set_async (struct checker * c) { if (!c) return; c->sync = 0; } void checker_enable (struct checker * c) { if (!c) return; c->disable = 0; } void checker_disable (struct checker * c) { if (!c) return; c->disable = 1; } int checker_init (struct checker * c, void ** mpctxt_addr) { if (!c) return 1; c->mpcontext = mpctxt_addr; return c->init(c); } void checker_put (struct checker * dst) { struct checker * src; if (!dst || !dst->check) return; src = checker_lookup(dst->name); if (dst->free) dst->free(dst); memset(dst, 0x0, sizeof(struct checker)); free_checker(src); } int checker_check (struct checker * c) { int r; if (!c) return PATH_WILD; c->message[0] = '\0'; if (c->disable) { MSG(c, "checker disabled"); return PATH_UNCHECKED; } if (c->fd <= 0) { MSG(c, "no usable fd"); return PATH_WILD; } r = c->check(c); return r; } int checker_selected (struct checker * c) { if (!c) return 0; return (c->check) ? 1 : 0; } char * checker_name (struct checker * c) { if (!c) return NULL; return c->name; } char * checker_message (struct checker * c) { if (!c) return NULL; return c->message; } void checker_clear_message (struct checker *c) { if (!c) return; c->message[0] = '\0'; } void checker_get (struct checker * dst, char * name) { struct checker * src = checker_lookup(name); if (!dst) return; if (!src) { dst->check = NULL; return; } dst->fd = src->fd; dst->sync = src->sync; strncpy(dst->name, src->name, CHECKER_NAME_LEN); strncpy(dst->message, src->message, CHECKER_MSG_LEN); dst->check = src->check; dst->init = src->init; dst->free = src->free; dst->handle = NULL; src->refcount++; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers.h000066400000000000000000000103361260771327300232120ustar00rootroot00000000000000#ifndef _CHECKERS_H #define _CHECKERS_H #include "list.h" #include "memory.h" /* * * Userspace (multipath/multipathd) path states * * PATH_WILD: * - Use: None of the checkers (returned if we don't have an fd) * - Description: Corner case where "fd <= 0" for path fd (see checker_check()) * * PATH_UNCHECKED: * - Use: Only in directio checker * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT * not include in flags, or O_DIRECT read fails * - Notes: * - multipathd: uses it to skip over paths in sync_map_state() * - multipath: used in update_paths(); if state==PATH_UNCHECKED, call * pathinfo() * * PATH_DOWN: * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) * - Description: Either a) SG_IO ioctl failed, or b) check condition on some * SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and * you shouldn't try to send commands to it * * PATH_UP: * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) * - Description: Path is up and I/O can be sent to it * * PATH_SHAKY: * - Use: Only emc_clariion * - Description: Indicates path not available for "normal" operations * * PATH_GHOST: * - Use: Only hp_sw and rdac * - Description: Indicates a "passive/standby" path on active/passive HP * arrays. These paths will return valid answers to certain SCSI commands * (tur, read_capacity, inquiry, start_stop), but will fail I/O commands. * The path needs an initialization command to be sent to it in order for * I/Os to succeed. * * PATH_PENDING: * - Use: All async checkers * - Description: Indicates a check IO is in flight. * * PATH_TIMEOUT: * - Use: Only tur checker * - Description: Command timed out * * PATH REMOVED: * - Use: All checkers * - Description: Device has been removed from the system * * PATH_DELAYED: * - Use: None of the checkers (returned if the path is being delayed before * reintegration. * - Description: If a path fails after being up for less than * delay_watch_checks checks, when it comes back up again, it will not * be marked as up until it has been up for delay_wait_checks checks. * During this time, it is marked as "delayed" */ enum path_check_state { PATH_WILD, PATH_UNCHECKED, PATH_DOWN, PATH_UP, PATH_SHAKY, PATH_GHOST, PATH_PENDING, PATH_TIMEOUT, PATH_REMOVED, PATH_DELAYED, PATH_MAX_STATE }; #define DIRECTIO "directio" #define TUR "tur" #define HP_SW "hp_sw" #define RDAC "rdac" #define EMC_CLARIION "emc_clariion" #define READSECTOR0 "readsector0" #define CCISS_TUR "cciss_tur" #define DEFAULT_CHECKER DIRECTIO #define ASYNC_TIMEOUT_SEC 30 /* * strings lengths */ #define CHECKER_NAME_LEN 16 #define CHECKER_MSG_LEN 256 #define CHECKER_DEV_LEN 256 #define LIB_CHECKER_NAMELEN 256 struct checker { struct list_head node; void *handle; int refcount; int fd; int sync; unsigned int timeout; int disable; char name[CHECKER_NAME_LEN]; char message[CHECKER_MSG_LEN]; /* comm with callers */ void * context; /* store for persistent data */ void ** mpcontext; /* store for persistent data shared multipath-wide. Use MALLOC if you want to stuff data in. */ int (*check)(struct checker *); int (*init)(struct checker *); /* to allocate the context */ void (*free)(struct checker *); /* to free the context */ }; #define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); char * checker_state_name (int); int init_checkers (void); void cleanup_checkers (void); struct checker * add_checker (char *); struct checker * checker_lookup (char *); int checker_init (struct checker *, void **); void checker_put (struct checker *); void checker_reset (struct checker *); void checker_set_sync (struct checker *); void checker_set_async (struct checker *); void checker_set_fd (struct checker *, int); void checker_enable (struct checker *); void checker_disable (struct checker *); int checker_check (struct checker *); int checker_selected (struct checker *); char * checker_name (struct checker *); char * checker_message (struct checker *); void checker_clear_message (struct checker *c); void checker_get (struct checker *, char *); #endif /* _CHECKERS_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/000077500000000000000000000000001260771327300230365ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/Makefile000066400000000000000000000012111260771327300244710ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../../Makefile.inc LIBS= \ libcheckcciss_tur.so \ libcheckreadsector0.so \ libchecktur.so \ libcheckdirectio.so \ libcheckemc_clariion.so \ libcheckhp_sw.so \ libcheckrdac.so CFLAGS += -I.. all: $(LIBS) libcheckdirectio.so: libsg.o directio.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio libcheck%.so: libsg.o %.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ install: $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir) uninstall: for file in $(LIBS); do rm -f $(DESTDIR)$(libdir)/$$file; done clean: rm -f core *.a *.o *.gz *.so multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/cciss.h000066400000000000000000000057231260771327300243220ustar00rootroot00000000000000#ifndef CCISS_H #define CCISS_H #include #include #define CCISS_IOC_MAGIC 'B' /* * transfer direction */ #define XFER_NONE 0x00 #define XFER_WRITE 0x01 #define XFER_READ 0x02 #define XFER_RSVD 0x03 /* * task attribute */ #define ATTR_UNTAGGED 0x00 #define ATTR_SIMPLE 0x04 #define ATTR_HEADOFQUEUE 0x05 #define ATTR_ORDERED 0x06 #define ATTR_ACA 0x07 /* * cdb type */ #define TYPE_CMD 0x00 #define TYPE_MSG 0x01 #define SENSEINFOBYTES 32 /* * Type defs used in the following structs */ #define BYTE __u8 #define WORD __u16 #define HWORD __u16 #define DWORD __u32 #pragma pack(1) //Command List Structure typedef union _SCSI3Addr_struct { struct { BYTE Dev; BYTE Bus:6; BYTE Mode:2; // b00 } PeripDev; struct { BYTE DevLSB; BYTE DevMSB:6; BYTE Mode:2; // b01 } LogDev; struct { BYTE Dev:5; BYTE Bus:3; BYTE Targ:6; BYTE Mode:2; // b10 } LogUnit; } SCSI3Addr_struct; typedef struct _PhysDevAddr_struct { DWORD TargetId:24; DWORD Bus:6; DWORD Mode:2; SCSI3Addr_struct Target[2]; //2 level target device addr } PhysDevAddr_struct; typedef struct _LogDevAddr_struct { DWORD VolId:30; DWORD Mode:2; BYTE reserved[4]; } LogDevAddr_struct; typedef union _LUNAddr_struct { BYTE LunAddrBytes[8]; SCSI3Addr_struct SCSI3Lun[4]; PhysDevAddr_struct PhysDev; LogDevAddr_struct LogDev; } LUNAddr_struct; typedef struct _RequestBlock_struct { BYTE CDBLen; struct { BYTE Type:3; BYTE Attribute:3; BYTE Direction:2; } Type; HWORD Timeout; BYTE CDB[16]; } RequestBlock_struct; typedef union _MoreErrInfo_struct{ struct { BYTE Reserved[3]; BYTE Type; DWORD ErrorInfo; }Common_Info; struct{ BYTE Reserved[2]; BYTE offense_size;//size of offending entry BYTE offense_num; //byte # of offense 0-base DWORD offense_value; }Invalid_Cmd; }MoreErrInfo_struct; typedef struct _ErrorInfo_struct { BYTE ScsiStatus; BYTE SenseLen; HWORD CommandStatus; DWORD ResidualCnt; MoreErrInfo_struct MoreErrInfo; BYTE SenseInfo[SENSEINFOBYTES]; } ErrorInfo_struct; #pragma pack() typedef struct _IOCTL_Command_struct { LUNAddr_struct LUN_info; RequestBlock_struct Request; ErrorInfo_struct error_info; WORD buf_size; /* size in bytes of the buf */ BYTE *buf; } IOCTL_Command_struct; typedef struct _LogvolInfo_struct{ __u32 LunID; int num_opens; /* number of opens on the logical volume */ int num_parts; /* number of partitions configured on logvol */ } LogvolInfo_struct; #define CCISS_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 11, IOCTL_Command_struct) #define CCISS_GETLUNINFO _IOR(CCISS_IOC_MAGIC, 17, LogvolInfo_struct) int cciss_init( struct checker *); void cciss_free (struct checker * c); int cciss_tur( struct checker *); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/cciss_tur.c000066400000000000000000000077261260771327300252140ustar00rootroot00000000000000/* ***************************************************************************** * * * (C) Copyright 2007 Hewlett-Packard Development Company, L.P * * * * 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., * * 675 Mass Ave, Cambridge, MA 02139, USA. * * * * The copy of the GNU General Public License is available at * * /opt/hp/HPDMmultipath-tool directoy * * * ***************************************************************************** */ /* * This program originally derived from and inspired by * Christophe Varoqui's tur.c, part of libchecker. */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "cciss.h" #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 #define MSG_CCISS_TUR_UP "cciss_tur checker reports path is up" #define MSG_CCISS_TUR_DOWN "cciss_tur checker reports path is down" struct cciss_tur_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } extern int libcheck_check (struct checker * c) { int rc; int ret; unsigned int lun = 0; struct cciss_tur_checker_context * ctxt = NULL; LogvolInfo_struct lvi; // logical "volume" info IOCTL_Command_struct cic; // cciss ioctl command if ((c->fd) <= 0) { MSG(c,"no usable fd"); ret = -1; goto out; } rc = ioctl(c->fd, CCISS_GETLUNINFO, &lvi); if ( rc != 0) { perror("Error: "); fprintf(stderr, "cciss TUR failed in CCISS_GETLUNINFO: %s\n", strerror(errno)); MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } else { lun = lvi.LunID; } memset(&cic, 0, sizeof(cic)); cic.LUN_info.LogDev.VolId = lun & 0x3FFFFFFF; cic.LUN_info.LogDev.Mode = 0x01; /* logical volume addressing */ cic.Request.CDBLen = 6; /* need to try just 2 bytes here */ cic.Request.Type.Type = TYPE_CMD; // It is a command. cic.Request.Type.Attribute = ATTR_SIMPLE; cic.Request.Type.Direction = XFER_NONE; cic.Request.Timeout = 0; cic.Request.CDB[0] = 0; cic.Request.CDB[1] = 0; cic.Request.CDB[2] = 0; cic.Request.CDB[3] = 0; cic.Request.CDB[4] = 0; cic.Request.CDB[5] = 0; rc = ioctl(c->fd, CCISS_PASSTHRU, &cic); if (rc < 0) { fprintf(stderr, "cciss TUR failed: %s\n", strerror(errno)); MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } if ((cic.error_info.CommandStatus | cic.error_info.ScsiStatus )) { MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } MSG(c,MSG_CCISS_TUR_UP); ret = PATH_UP; out: /* * caller told us he doesn't want to keep the context : * free it */ if (!c->context) free(ctxt); return(ret); } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/directio.c000066400000000000000000000105511260771327300250060ustar00rootroot00000000000000/* * Copyright (c) 2005 Hannes Reinecke, Suse */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/debug.h" #define MSG_DIRECTIO_UNKNOWN "directio checker is not available" #define MSG_DIRECTIO_UP "directio checker reports path is up" #define MSG_DIRECTIO_DOWN "directio checker reports path is down" #define MSG_DIRECTIO_PENDING "directio checker is waiting on aio" #define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args) struct directio_context { int running; int reset_flags; int blksize; unsigned char * buf; unsigned char * ptr; io_context_t ioctx; struct iocb io; }; int libcheck_init (struct checker * c) { unsigned long pgsize = getpagesize(); struct directio_context * ct; long flags; ct = malloc(sizeof(struct directio_context)); if (!ct) return 1; memset(ct, 0, sizeof(struct directio_context)); if (io_setup(1, &ct->ioctx) != 0) { condlog(1, "io_setup failed"); free(ct); return 1; } if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { MSG(c, "cannot get blocksize, set default"); ct->blksize = 512; } if (ct->blksize > 4096) { /* * Sanity check for DASD; BSZGET is broken */ ct->blksize = 4096; } if (!ct->blksize) goto out; ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); if (!ct->buf) goto out; flags = fcntl(c->fd, F_GETFL); if (flags < 0) goto out; if (!(flags & O_DIRECT)) { flags |= O_DIRECT; if (fcntl(c->fd, F_SETFL, flags) < 0) goto out; ct->reset_flags = 1; } ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) & (~(pgsize - 1))); /* Sucessfully initialized, return the context. */ c->context = (void *) ct; return 0; out: if (ct->buf) free(ct->buf); io_destroy(ct->ioctx); free(ct); return 1; } void libcheck_free (struct checker * c) { struct directio_context * ct = (struct directio_context *)c->context; long flags; if (!ct) return; if (ct->reset_flags) { if ((flags = fcntl(c->fd, F_GETFL)) >= 0) { flags &= ~O_DIRECT; /* No point in checking for errors */ fcntl(c->fd, F_SETFL, flags); } } if (ct->buf) free(ct->buf); io_destroy(ct->ioctx); free(ct); } static int check_state(int fd, struct directio_context *ct, int sync, int timeout_secs) { struct timespec timeout = { .tv_nsec = 5 }; struct io_event event; struct stat sb; int rc = PATH_UNCHECKED; long r; if (fstat(fd, &sb) == 0) { LOG(4, "called for %x", (unsigned) sb.st_rdev); } if (sync > 0) { LOG(4, "called in synchronous mode"); timeout.tv_sec = timeout_secs; timeout.tv_nsec = 0; } if (!ct->running) { struct iocb *ios[1] = { &ct->io }; LOG(3, "starting new request"); memset(&ct->io, 0, sizeof(struct iocb)); io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0); if (io_submit(ct->ioctx, 1, ios) != 1) { LOG(3, "io_submit error %i", errno); return PATH_UNCHECKED; } } ct->running++; errno = 0; r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout); if (r < 0 ) { LOG(3, "async io getevents returned %li (errno=%s)", r, strerror(errno)); ct->running = 0; rc = PATH_UNCHECKED; } else if (r < 1L) { if (ct->running > timeout_secs || sync) { struct iocb *ios[1] = { &ct->io }; LOG(3, "abort check on timeout"); r = io_cancel(ct->ioctx, ios[0], &event); /* * Only reset ct->running if we really * could abort the pending I/O */ if (r) LOG(3, "io_cancel error %i", errno); else ct->running = 0; rc = PATH_DOWN; } else { LOG(3, "async io pending"); rc = PATH_PENDING; } } else { LOG(3, "io finished %lu/%lu", event.res, event.res2); ct->running = 0; rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; } return rc; } int libcheck_check (struct checker * c) { int ret; struct directio_context * ct = (struct directio_context *)c->context; if (!ct) return PATH_UNCHECKED; ret = check_state(c->fd, ct, c->sync, c->timeout); switch (ret) { case PATH_UNCHECKED: MSG(c, MSG_DIRECTIO_UNKNOWN); break; case PATH_DOWN: MSG(c, MSG_DIRECTIO_DOWN); break; case PATH_UP: MSG(c, MSG_DIRECTIO_UP); break; case PATH_PENDING: MSG(c, MSG_DIRECTIO_PENDING); break; default: break; } return ret; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/directio.h000066400000000000000000000002611260771327300250100ustar00rootroot00000000000000#ifndef _DIRECTIO_H #define _DIRECTIO_H int directio (struct checker *); int directio_init (struct checker *); void directio_free (struct checker *); #endif /* _DIRECTIO_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/emc_clariion.c000066400000000000000000000141561260771327300256350ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Lars Marowsky-Bree */ #include #include #include #include #include #include #include #include #include #include #include "../libmultipath/sg_include.h" #include "libsg.h" #include "checkers.h" #include "debug.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define HEAVY_CHECK_COUNT 10 /* * Mechanism to track CLARiiON inactive snapshot LUs. * This is done so that we can fail passive paths * to an inactive snapshot LU even though since a * simple read test would return 02/04/03 instead * of 05/25/01 sensekey/ASC/ASCQ data. */ #define IS_INACTIVE_SNAP(c) (c->mpcontext ? \ ((struct emc_clariion_checker_LU_context *) \ (*c->mpcontext))->inactive_snap \ : 0) #define SET_INACTIVE_SNAP(c) if (c->mpcontext) \ ((struct emc_clariion_checker_LU_context *)\ (*c->mpcontext))->inactive_snap = 1 #define CLR_INACTIVE_SNAP(c) if (c->mpcontext) \ ((struct emc_clariion_checker_LU_context *)\ (*c->mpcontext))->inactive_snap = 0 struct emc_clariion_checker_path_context { char wwn[16]; unsigned wwn_set; }; struct emc_clariion_checker_LU_context { int inactive_snap; }; extern void hexadecimal_to_ascii(char * wwn, char *wwnstr) { int i,j, nbl; for (i=0,j=0;i<16;i++) { wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ? '0' + nbl : 'a' + (nbl - 10); wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ? '0' + nbl : 'a' + (nbl - 10); } wwnstr[32]=0; } int libcheck_init (struct checker * c) { /* * Allocate and initialize the path specific context. */ c->context = MALLOC(sizeof(struct emc_clariion_checker_path_context)); if (!c->context) return 1; ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; /* * Allocate and initialize the multi-path global context. */ if (c->mpcontext && *c->mpcontext == NULL) { void * mpctxt = malloc(sizeof(int)); *c->mpcontext = mpctxt; CLR_INACTIVE_SNAP(c); } return 0; } void libcheck_free (struct checker * c) { free(c->context); } int libcheck_check (struct checker * c) { unsigned char sense_buffer[128] = { 0, }; unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; struct emc_clariion_checker_path_context * ct = (struct emc_clariion_checker_path_context *)c->context; char wwnstr[33]; int ret; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_buffer, 0, 128); memset(sb, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = c->timeout * 1000; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, "emc_clariion_checker: sending query command failed"); return PATH_DOWN; } if (io_hdr.info & SG_INFO_OK_MASK) { MSG(c, "emc_clariion_checker: query command indicates error"); return PATH_DOWN; } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { MSG(c, "emc_clariion_checker: Path unit report page in " "unknown format"); return PATH_DOWN; } if ( /* Effective initiator type */ sense_buffer[27] != 0x03 /* * Failover mode should be set to 1 (PNR failover mode) * or 4 (ALUA failover mode). */ || (((sense_buffer[28] & 0x07) != 0x04) && ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { MSG(c, "emc_clariion_checker: Path not correctly configured " "for failover"); return PATH_DOWN; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { MSG(c, "emc_clariion_checker: Path not available for normal " "operations"); return PATH_SHAKY; } if ( /* LUN should at least be bound somewhere and not be LUNZ */ sense_buffer[4] == 0x00) { MSG(c, "emc_clariion_checker: Logical Unit is unbound " "or LUNZ"); return PATH_DOWN; } /* * store the LUN WWN there and compare that it indeed did not * change in between, to protect against the path suddenly * pointing somewhere else. */ if (ct->wwn_set) { if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { MSG(c, "emc_clariion_checker: Logical Unit WWN " "has changed!"); return PATH_DOWN; } } else { memcpy(ct->wwn, &sense_buffer[10], 16); ct->wwn_set = 1; } /* * Issue read on active path to determine if inactive snapshot. */ if (sense_buffer[4] == 2) {/* if active path */ unsigned char buf[4096]; memset(buf, 0, 4096); ret = sg_read(c->fd, &buf[0], 4096, sbb = &sb[0], SENSE_BUFF_LEN, c->timeout); if (ret == PATH_DOWN) { hexadecimal_to_ascii(ct->wwn, wwnstr); /* * Check for inactive snapshot LU this way. Must * fail these. */ if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && (sbb[13]==1)) { /* * Do this so that we can fail even the * passive paths which will return * 02/04/03 not 05/25/01 on read. */ SET_INACTIVE_SNAP(c); condlog(3, "emc_clariion_checker: Active " "path to inactive snapshot WWN %s.", wwnstr); } else MSG(c, "emc_clariion_checker: Read " "error for WWN %s. Sense data are " "0x%x/0x%x/0x%x.", wwnstr, sbb[2]&0xf, sbb[12], sbb[13]); } else { MSG(c, "emc_clariion_checker: Active path is " "healthy."); /* * Remove the path from the set of paths to inactive * snapshot LUs if it was in this list since the * snapshot is no longer inactive. */ CLR_INACTIVE_SNAP(c); } } else { if (IS_INACTIVE_SNAP(c)) { hexadecimal_to_ascii(ct->wwn, wwnstr); condlog(3, "emc_clariion_checker: Passive " "path to inactive snapshot WWN %s.", wwnstr); ret = PATH_DOWN; } else { MSG(c, "emc_clariion_checker: Passive path is healthy."); ret = PATH_UP; /* not ghost */ } } return ret; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/emc_clariion.h000066400000000000000000000003111260771327300256260ustar00rootroot00000000000000#ifndef _EMC_CLARIION_H #define _EMC_CLARIION_H int emc_clariion (struct checker *); int emc_clariion_init (struct checker *); void emc_clariion_free (struct checker *); #endif /* _EMC_CLARIION_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/hp_sw.c000066400000000000000000000065331260771327300243310ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/sg_include.h" #define TUR_CMD_LEN 6 #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define HEAVY_CHECK_COUNT 10 #define MSG_HP_SW_UP "hp_sw checker reports path is up" #define MSG_HP_SW_DOWN "hp_sw checker reports path is down" #define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost" struct sw_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } static int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len, int noisy, unsigned int timeout) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; if (cmddt) inqCmdBlk[1] |= 2; if (evpd) inqCmdBlk[1] |= 1; inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if(RECOVERED_ERROR == sense_key) return 0; } } return 1; } static int do_tur (int fd, unsigned int timeout) { unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; struct sg_io_hdr io_hdr; unsigned char sense_buffer[32]; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) return 1; if (io_hdr.info & SG_INFO_OK_MASK) return 1; return 0; } extern int libcheck_check (struct checker * c) { char buff[MX_ALLOC_LEN]; if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout)) { MSG(c, MSG_HP_SW_DOWN); return PATH_DOWN; } if (do_tur(c->fd, c->timeout)) { MSG(c, MSG_HP_SW_GHOST); return PATH_GHOST; } MSG(c, MSG_HP_SW_UP); return PATH_UP; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/hp_sw.h000066400000000000000000000002371260771327300243310ustar00rootroot00000000000000#ifndef _HP_SW_H #define _HP_SW_H int hp_sw (struct checker *); int hp_sw_init (struct checker *); void hp_sw_free (struct checker *); #endif /* _HP_SW_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/libsg.c000066400000000000000000000044001260771327300243000ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include #include "checkers.h" #include "libsg.h" #include "../libmultipath/sg_include.h" int sg_read (int sg_fd, unsigned char * buff, int buff_len, unsigned char * sense, int sense_len, unsigned int timeout) { /* defaults */ int blocks; long long start_block = 0; int bs = 512; int cdbsz = 10; int * diop = NULL; unsigned char rdCmd[cdbsz]; unsigned char *sbb = sense; struct sg_io_hdr io_hdr; int res; int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; int sz_ind; struct stat filestatus; int retry_count = 3; if (fstat(sg_fd, &filestatus) != 0) return PATH_DOWN; bs = (filestatus.st_blksize > 4096)? 4096: filestatus.st_blksize; blocks = buff_len / bs; memset(rdCmd, 0, cdbsz); sz_ind = 1; rdCmd[0] = rd_opcode[sz_ind]; rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); rdCmd[5] = (unsigned char)(start_block & 0xff); rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); rdCmd[8] = (unsigned char)(blocks & 0xff); memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = rdCmd; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = bs * blocks; io_hdr.dxferp = buff; io_hdr.mx_sb_len = sense_len; io_hdr.sbp = sense; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = (int)start_block; if (diop && *diop) io_hdr.flags |= SG_FLAG_DIRECT_IO; retry: memset(sense, 0, sense_len); while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); if (res < 0) { if (ENOMEM == errno) { return PATH_UP; } return PATH_DOWN; } if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) { return PATH_UP; } else { int key = 0; if (io_hdr.sb_len_wr > 3) { if (sbb[0] == 0x72 || sbb[0] == 0x73) key = sbb[1] & 0x0f; else if (io_hdr.sb_len_wr > 13 && ((sbb[0] & 0x7f) == 0x70 || (sbb[0] & 0x7f) == 0x71)) key = sbb[2] & 0x0f; } /* * Retry if UNIT_ATTENTION check condition. */ if (key == 0x6) { if (--retry_count) goto retry; } return PATH_DOWN; } } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/libsg.h000066400000000000000000000003241260771327300243060ustar00rootroot00000000000000#ifndef _LIBSG_H #define _LIBSG_H #define SENSE_BUFF_LEN 32 int sg_read (int sg_fd, unsigned char * buff, int buff_len, unsigned char * sense, int sense_len, unsigned int timeout); #endif /* _LIBSG_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/rdac.c000066400000000000000000000176231260771327300241240ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "debug.h" #include "../libmultipath/sg_include.h" #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define MODE_SENSE_CMD 0x5a #define MODE_SELECT_CMD 0x55 #define MODE_SEN_SEL_CMDLEN 10 #define SENSE_BUFF_LEN 32 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define CURRENT_PAGE_CODE_VALUES 0 #define CHANGEABLE_PAGE_CODE_VALUES 1 #define MSG_RDAC_UP "rdac checker reports path is up" #define MSG_RDAC_DOWN "rdac checker reports path is down" #define MSG_RDAC_GHOST "rdac checker reports path is ghost" #define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR #define RTPG_UNAVAILABLE 0x3 #define RTPG_OFFLINE 0xE #define RTPG_TRANSITIONING 0xF #define RTPG_UNAVAIL_NON_RESPONSIVE 0x2 #define RTPG_UNAVAIL_IN_RESET 0x3 #define RTPG_UNAVAIL_CFW_DL1 0x4 #define RTPG_UNAVAIL_CFW_DL2 0x5 #define RTPG_UNAVAIL_QUIESCED 0x6 #define RTPG_UNAVAIL_SERVICE_MODE 0x7 struct control_mode_page { unsigned char header[8]; unsigned char page_code; unsigned char page_len; unsigned char dontcare0[3]; unsigned char tas_bit; unsigned char dontcare1[6]; }; struct rdac_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { unsigned char cmd[MODE_SEN_SEL_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; struct control_mode_page current, changeable; int set = 0; memset(cmd, 0, MODE_SEN_SEL_CMDLEN); cmd[0] = MODE_SENSE_CMD; cmd[1] = 0x08; /* DBD bit on */ cmd[2] = 0xA + (CURRENT_PAGE_CODE_VALUES << 6); cmd[8] = (sizeof(struct control_mode_page) & 0xff); memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); memset(¤t, 0, sizeof(struct control_mode_page)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MODE_SEN_SEL_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = (sizeof(struct control_mode_page) & 0xff); io_hdr.dxferp = ¤t; io_hdr.cmdp = cmd; io_hdr.sbp = sense_b; io_hdr.timeout = c->timeout * 1000; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* check the TAS bit to see if it is already set */ if ((current.tas_bit >> 6) & 0x1) { set = 1; goto out; } /* get the changeble values */ cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6); io_hdr.dxferp = &changeable; memset(&changeable, 0, sizeof(struct control_mode_page)); if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* if TAS bit is not settable exit */ if (((changeable.tas_bit >> 6) & 0x1) == 0) goto out; /* Now go ahead and set it */ memset(cmd, 0, MODE_SEN_SEL_CMDLEN); cmd[0] = MODE_SELECT_CMD; cmd[1] = 0x1; /* set SP bit on */ cmd[8] = (sizeof(struct control_mode_page) & 0xff); /* use the same buffer as current, only set the tas bit */ current.page_code = 0xA; current.page_len = 0xA; current.tas_bit |= (1 << 6); io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.dxferp = ¤t; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* Success */ set = 1; out: if (set == 0) condlog(3, "rdac checker failed to set TAS bit"); return 0; } void libcheck_free (struct checker * c) { return; } static int do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len, unsigned int timeout) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; int retry_rdac = 5; retry: inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; /* check if we need to retry this error */ if (io_hdr.info & SG_INFO_OK_MASK) { switch (io_hdr.host_status) { case DID_BUS_BUSY: case DID_ERROR: case DID_SOFT_ERROR: case DID_TRANSPORT_DISRUPTED: /* Transport error, retry */ if (--retry_rdac) goto retry; break; default: break; } } if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if (RECOVERED_ERROR == sense_key) return 0; } } return 1; } struct volume_access_inq { char PQ_PDT; char dontcare0[7]; char avtcvp; char vol_ppp; char aas_cur; char vendor_specific_cur; char aas_alt; char vendor_specific_alt; char dontcare1[34]; }; const char *checker_msg_string(struct volume_access_inq *inq) { /* lun not connected */ if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f)) return MSG_RDAC_DOWN_TYPE("lun not connected"); /* if no tpg data is available, give the generic path down message */ if (!(inq->avtcvp & 0x10)) return MSG_RDAC_DOWN; /* controller is booting up */ if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) && (inq->aas_alt & 0x0F) != RTPG_TRANSITIONING) return MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"); /* if not unavailable, give generic message */ if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE) return MSG_RDAC_DOWN; /* target port group unavailable */ switch (inq->vendor_specific_cur) { case RTPG_UNAVAIL_NON_RESPONSIVE: return MSG_RDAC_DOWN_TYPE("non-responsive to queries"); case RTPG_UNAVAIL_IN_RESET: return MSG_RDAC_DOWN_TYPE("ctlr held in reset"); case RTPG_UNAVAIL_CFW_DL1: case RTPG_UNAVAIL_CFW_DL2: return MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"); case RTPG_UNAVAIL_QUIESCED: return MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"); case RTPG_UNAVAIL_SERVICE_MODE: return MSG_RDAC_DOWN_TYPE("ctlr is in service mode"); default: return MSG_RDAC_DOWN_TYPE("ctlr is unavailable"); } } extern int libcheck_check (struct checker * c) { struct volume_access_inq inq; int ret, inqfail; inqfail = 0; memset(&inq, 0, sizeof(struct volume_access_inq)); if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq), c->timeout)) { ret = PATH_DOWN; inqfail = 1; goto done; } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) { /* LUN not connected*/ ret = PATH_DOWN; goto done; } /* If TPGDE bit set, evaluate TPG information */ if ((inq.avtcvp & 0x10)) { switch (inq.aas_cur & 0x0F) { /* Never use the path if it reports unavailable */ case RTPG_UNAVAILABLE: ret = PATH_DOWN; goto done; /* * If both controllers report transitioning, it * means mode select or STPG is being processed. * * If this controller alone is transitioning, it's * booting and we shouldn't use it yet. */ case RTPG_TRANSITIONING: if ((inq.aas_alt & 0xF) != RTPG_TRANSITIONING) { ret = PATH_DOWN; goto done; } break; } } /* If owner set or ioship mode is enabled return PATH_UP always */ if ((inq.avtcvp & 0x1) || ((inq.avtcvp >> 5) & 0x1)) ret = PATH_UP; else ret = PATH_GHOST; done: switch (ret) { case PATH_DOWN: MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") : checker_msg_string(&inq)); break; case PATH_UP: MSG(c, MSG_RDAC_UP); break; case PATH_GHOST: MSG(c, MSG_RDAC_GHOST); break; } return ret; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/rdac.h000066400000000000000000000002261260771327300241200ustar00rootroot00000000000000#ifndef _RDAC_H #define _RDAC_H int rdac(struct checker *); int rdac_init(struct checker *); void rdac_free(struct checker *); #endif /* _RDAC_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/readsector0.c000066400000000000000000000014251260771327300254170ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include "checkers.h" #include "libsg.h" #define MSG_READSECTOR0_UP "readsector0 checker reports path is up" #define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" struct readsector0_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } int libcheck_check (struct checker * c) { unsigned char buf[4096]; unsigned char sbuf[SENSE_BUFF_LEN]; int ret; ret = sg_read(c->fd, &buf[0], 4069, &sbuf[0], SENSE_BUFF_LEN, c->timeout); switch (ret) { case PATH_DOWN: MSG(c, MSG_READSECTOR0_DOWN); break; case PATH_UP: MSG(c, MSG_READSECTOR0_UP); break; default: break; } return ret; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/readsector0.h000066400000000000000000000003031260771327300254160ustar00rootroot00000000000000#ifndef _READSECTOR0_H #define _READSECTOR0_H int readsector0 (struct checker *); int readsector0_init (struct checker *); void readsector0_free (struct checker *); #endif /* _READSECTOR0_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/tur.c000066400000000000000000000202741260771327300240210ustar00rootroot00000000000000/* * Some code borrowed from sg-utils. * * Copyright (c) 2004 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/debug.h" #include "../libmultipath/sg_include.h" #include "../libmultipath/uevent.h" #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 #define MSG_TUR_UP "tur checker reports path is up" #define MSG_TUR_DOWN "tur checker reports path is down" #define MSG_TUR_GHOST "tur checker reports path is in standby state" #define MSG_TUR_RUNNING "tur checker still running" #define MSG_TUR_TIMEOUT "tur checker timed out" #define MSG_TUR_FAILED "tur checker failed to initialize" struct tur_checker_context { dev_t devt; int state; int running; int fd; unsigned int timeout; time_t time; pthread_t thread; pthread_mutex_t lock; pthread_cond_t active; pthread_spinlock_t hldr_lock; int holders; char message[CHECKER_MSG_LEN]; }; #define TUR_DEVT(c) major((c)->devt), minor((c)->devt) int libcheck_init (struct checker * c) { struct tur_checker_context *ct; ct = malloc(sizeof(struct tur_checker_context)); if (!ct) return 1; memset(ct, 0, sizeof(struct tur_checker_context)); ct->state = PATH_UNCHECKED; ct->fd = -1; ct->holders = 1; pthread_cond_init(&ct->active, NULL); pthread_mutex_init(&ct->lock, NULL); pthread_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE); c->context = ct; return 0; } void cleanup_context(struct tur_checker_context *ct) { pthread_mutex_destroy(&ct->lock); pthread_cond_destroy(&ct->active); pthread_spin_destroy(&ct->hldr_lock); free(ct); } void libcheck_free (struct checker * c) { if (c->context) { struct tur_checker_context *ct = c->context; int holders; pthread_t thread; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; thread = ct->thread; pthread_spin_unlock(&ct->hldr_lock); if (holders) pthread_cancel(thread); else cleanup_context(ct); c->context = NULL; } return; } #define TUR_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args); int tur_check(int fd, unsigned int timeout, char *msg) { struct sg_io_hdr io_hdr; unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; unsigned char sense_buffer[32]; int retry_tur = 5; retry: memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(&sense_buffer, 0, 32); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { TUR_MSG(msg, MSG_TUR_DOWN); return PATH_DOWN; } if ((io_hdr.status & 0x7e) == 0x18) { /* * SCSI-3 arrays might return * reservation conflict on TUR */ TUR_MSG(msg, MSG_TUR_UP); return PATH_UP; } if (io_hdr.info & SG_INFO_OK_MASK) { int key = 0, asc, ascq; switch (io_hdr.host_status) { case DID_OK: case DID_NO_CONNECT: case DID_BAD_TARGET: case DID_ABORT: case DID_TRANSPORT_FAILFAST: break; default: /* Driver error, retry */ if (--retry_tur) goto retry; break; } if (io_hdr.sb_len_wr > 3) { if (io_hdr.sbp[0] == 0x72 || io_hdr.sbp[0] == 0x73) { key = io_hdr.sbp[1] & 0x0f; asc = io_hdr.sbp[2]; ascq = io_hdr.sbp[3]; } else if (io_hdr.sb_len_wr > 13 && ((io_hdr.sbp[0] & 0x7f) == 0x70 || (io_hdr.sbp[0] & 0x7f) == 0x71)) { key = io_hdr.sbp[2] & 0x0f; asc = io_hdr.sbp[12]; ascq = io_hdr.sbp[13]; } } if (key == 0x6) { /* Unit Attention, retry */ if (--retry_tur) goto retry; } else if (key == 0x2) { /* Not Ready */ /* Note: Other ALUA states are either UP or DOWN */ if( asc == 0x04 && ascq == 0x0b){ /* * LOGICAL UNIT NOT ACCESSIBLE, * TARGET PORT IN STANDBY STATE */ TUR_MSG(msg, MSG_TUR_GHOST); return PATH_GHOST; } } TUR_MSG(msg, MSG_TUR_DOWN); return PATH_DOWN; } TUR_MSG(msg, MSG_TUR_UP); return PATH_UP; } #define tur_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct) #define tur_thread_cleanup_pop(ct) pthread_cleanup_pop(1) void cleanup_func(void *data) { int holders; struct tur_checker_context *ct = data; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; ct->thread = 0; pthread_spin_unlock(&ct->hldr_lock); if (!holders) cleanup_context(ct); } void *tur_thread(void *ctx) { struct tur_checker_context *ct = ctx; int state; condlog(3, "%d:%d: tur checker starting up", TUR_DEVT(ct)); ct->message[0] = '\0'; /* This thread can be canceled, so setup clean up */ tur_thread_cleanup_push(ct) /* TUR checker start up */ pthread_mutex_lock(&ct->lock); ct->state = PATH_PENDING; pthread_mutex_unlock(&ct->lock); state = tur_check(ct->fd, ct->timeout, ct->message); /* TUR checker done */ pthread_mutex_lock(&ct->lock); ct->state = state; pthread_mutex_unlock(&ct->lock); pthread_cond_signal(&ct->active); condlog(3, "%d:%d: tur checker finished, state %s", TUR_DEVT(ct), checker_state_name(state)); tur_thread_cleanup_pop(ct); return ((void *)0); } void tur_timeout(struct timespec *tsp) { struct timeval now; gettimeofday(&now, NULL); tsp->tv_sec = now.tv_sec; tsp->tv_nsec = now.tv_usec * 1000; tsp->tv_nsec += 1000000; /* 1 millisecond */ } void tur_set_async_timeout(struct checker *c) { struct tur_checker_context *ct = c->context; struct timeval now; gettimeofday(&now, NULL); ct->time = now.tv_sec + c->timeout; } int tur_check_async_timeout(struct checker *c) { struct tur_checker_context *ct = c->context; struct timeval now; gettimeofday(&now, NULL); return (now.tv_sec > ct->time); } extern int libcheck_check (struct checker * c) { struct tur_checker_context *ct = c->context; struct timespec tsp; struct stat sb; pthread_attr_t attr; int tur_status, r; if (!ct) return PATH_UNCHECKED; if (fstat(c->fd, &sb) == 0) ct->devt = sb.st_rdev; if (c->sync) return tur_check(c->fd, c->timeout, c->message); /* * Async mode */ r = pthread_mutex_lock(&ct->lock); if (r != 0) { condlog(2, "%d:%d: tur mutex lock failed with %d", TUR_DEVT(ct), r); MSG(c, MSG_TUR_FAILED); return PATH_WILD; } if (ct->running) { /* Check if TUR checker is still running */ if (ct->thread) { if (tur_check_async_timeout(c)) { condlog(3, "%d:%d: tur checker timeout", TUR_DEVT(ct)); pthread_cancel(ct->thread); ct->running = 0; MSG(c, MSG_TUR_TIMEOUT); tur_status = PATH_TIMEOUT; } else { condlog(3, "%d:%d: tur checker not finished", TUR_DEVT(ct)); ct->running++; tur_status = PATH_PENDING; } } else { /* TUR checker done */ ct->running = 0; tur_status = ct->state; strncpy(c->message, ct->message, CHECKER_MSG_LEN); c->message[CHECKER_MSG_LEN - 1] = '\0'; } pthread_mutex_unlock(&ct->lock); } else { if (ct->thread) { /* pthread cancel failed. continue in sync mode */ pthread_mutex_unlock(&ct->lock); condlog(3, "%d:%d: tur thread not responding", TUR_DEVT(ct)); return PATH_TIMEOUT; } /* Start new TUR checker */ ct->state = PATH_UNCHECKED; ct->fd = c->fd; ct->timeout = c->timeout; pthread_spin_lock(&ct->hldr_lock); ct->holders++; pthread_spin_unlock(&ct->hldr_lock); tur_set_async_timeout(c); setup_thread_attr(&attr, 32 * 1024, 1); r = pthread_create(&ct->thread, &attr, tur_thread, ct); if (r) { pthread_mutex_unlock(&ct->lock); ct->thread = 0; ct->holders--; condlog(3, "%d:%d: failed to start tur thread, using" " sync mode", TUR_DEVT(ct)); return tur_check(c->fd, c->timeout, c->message); } pthread_attr_destroy(&attr); tur_timeout(&tsp); r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); tur_status = ct->state; strncpy(c->message, ct->message,CHECKER_MSG_LEN); c->message[CHECKER_MSG_LEN -1] = '\0'; pthread_mutex_unlock(&ct->lock); if (ct->thread && (tur_status == PATH_PENDING || tur_status == PATH_UNCHECKED)) { condlog(3, "%d:%d: tur checker still running", TUR_DEVT(ct)); ct->running = 1; tur_status = PATH_PENDING; } } return tur_status; } multipath-tools-0.5.0+git1.656f8865/libmultipath/checkers/tur.h000066400000000000000000000002231260771327300240160ustar00rootroot00000000000000#ifndef _TUR_H #define _TUR_H int tur (struct checker *); int tur_init (struct checker *); void tur_free (struct checker *); #endif /* _TUR_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/config.c000066400000000000000000000351061260771327300226650ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include "checkers.h" #include "memory.h" #include "util.h" #include "debug.h" #include "parser.h" #include "dict.h" #include "hwtable.h" #include "vector.h" #include "structs.h" #include "config.h" #include "blacklist.h" #include "defaults.h" #include "prio.h" #include "devmapper.h" static int hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) { if ((hwe2->vendor && !hwe1->vendor) || (hwe1->vendor && (!hwe2->vendor || strcmp(hwe1->vendor, hwe2->vendor)))) return 1; if ((hwe2->product && !hwe1->product) || (hwe1->product && (!hwe2->product || strcmp(hwe1->product, hwe2->product)))) return 1; if ((hwe2->revision && !hwe1->revision) || (hwe1->revision && (!hwe2->revision || strcmp(hwe1->revision, hwe2->revision)))) return 1; return 0; } static struct hwentry * find_hwe_strmatch (vector hwtable, struct hwentry *hwe) { int i; struct hwentry *tmp, *ret = NULL; vector_foreach_slot (hwtable, tmp, i) { if (hwe_strmatch(tmp, hwe)) continue; ret = tmp; break; } return ret; } static int hwe_regmatch (struct hwentry *hwe1, struct hwentry *hwe2) { regex_t vre, pre, rre; int retval = 1; if (hwe1->vendor && regcomp(&vre, hwe1->vendor, REG_EXTENDED|REG_NOSUB)) goto out; if (hwe1->product && regcomp(&pre, hwe1->product, REG_EXTENDED|REG_NOSUB)) goto out_vre; if (hwe1->revision && regcomp(&rre, hwe1->revision, REG_EXTENDED|REG_NOSUB)) goto out_pre; if ((!hwe1->vendor || !hwe2->vendor || !regexec(&vre, hwe2->vendor, 0, NULL, 0)) && (!hwe1->product || !hwe2->product || !regexec(&pre, hwe2->product, 0, NULL, 0)) && (!hwe1->revision || !hwe2->revision || !regexec(&rre, hwe2->revision, 0, NULL, 0))) retval = 0; if (hwe1->revision) regfree(&rre); out_pre: if (hwe1->product) regfree(&pre); out_vre: if (hwe1->vendor) regfree(&vre); out: return retval; } struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char * revision) { int i; struct hwentry hwe, *tmp, *ret = NULL; hwe.vendor = vendor; hwe.product = product; hwe.revision = revision; /* * Search backwards here. * User modified entries are attached at the end of * the list, so we have to check them first before * continuing to the generic entries */ vector_foreach_slot_backwards (hwtable, tmp, i) { if (hwe_regmatch(tmp, &hwe)) continue; ret = tmp; break; } return ret; } extern struct mpentry * find_mpe (char * wwid) { int i; struct mpentry * mpe; if (!wwid) return NULL; vector_foreach_slot (conf->mptable, mpe, i) if (mpe->wwid && !strcmp(mpe->wwid, wwid)) return mpe; return NULL; } extern char * get_mpe_wwid (char * alias) { int i; struct mpentry * mpe; if (!alias) return NULL; vector_foreach_slot (conf->mptable, mpe, i) if (mpe->alias && strcmp(mpe->alias, alias) == 0) return mpe->wwid; return NULL; } void free_hwe (struct hwentry * hwe) { if (!hwe) return; if (hwe->vendor) FREE(hwe->vendor); if (hwe->product) FREE(hwe->product); if (hwe->revision) FREE(hwe->revision); if (hwe->getuid) FREE(hwe->getuid); if (hwe->uid_attribute) FREE(hwe->uid_attribute); if (hwe->features) FREE(hwe->features); if (hwe->hwhandler) FREE(hwe->hwhandler); if (hwe->selector) FREE(hwe->selector); if (hwe->checker_name) FREE(hwe->checker_name); if (hwe->prio_name) FREE(hwe->prio_name); if (hwe->prio_args) FREE(hwe->prio_args); if (hwe->alias_prefix) FREE(hwe->alias_prefix); if (hwe->bl_product) FREE(hwe->bl_product); FREE(hwe); } void free_hwtable (vector hwtable) { int i; struct hwentry * hwe; if (!hwtable) return; vector_foreach_slot (hwtable, hwe, i) free_hwe(hwe); vector_free(hwtable); } void free_mpe (struct mpentry * mpe) { if (!mpe) return; if (mpe->wwid) FREE(mpe->wwid); if (mpe->selector) FREE(mpe->selector); if (mpe->getuid) FREE(mpe->getuid); if (mpe->uid_attribute) FREE(mpe->uid_attribute); if (mpe->alias) FREE(mpe->alias); if (mpe->prio_name) FREE(mpe->prio_name); if (mpe->prio_args) FREE(mpe->prio_args); FREE(mpe); } void free_mptable (vector mptable) { int i; struct mpentry * mpe; if (!mptable) return; vector_foreach_slot (mptable, mpe, i) free_mpe(mpe); vector_free(mptable); } struct mpentry * alloc_mpe (void) { struct mpentry * mpe = (struct mpentry *) MALLOC(sizeof(struct mpentry)); return mpe; } struct hwentry * alloc_hwe (void) { struct hwentry * hwe = (struct hwentry *) MALLOC(sizeof(struct hwentry)); return hwe; } static char * set_param_str(char * str) { char * dst; int len; if (!str) return NULL; len = strlen(str); if (!len) return NULL; dst = (char *)MALLOC(len + 1); if (!dst) return NULL; strcpy(dst, str); return dst; } #define merge_str(s) \ if (!dst->s && src->s) { \ if (!(dst->s = set_param_str(src->s))) \ return 1; \ } #define merge_num(s) \ if (!dst->s && src->s) \ dst->s = src->s static int merge_hwe (struct hwentry * dst, struct hwentry * src) { merge_str(vendor); merge_str(product); merge_str(revision); merge_str(getuid); merge_str(uid_attribute); merge_str(features); merge_str(hwhandler); merge_str(selector); merge_str(checker_name); merge_str(prio_name); merge_str(prio_args); merge_str(alias_prefix); merge_str(bl_product); merge_num(pgpolicy); merge_num(pgfailback); merge_num(rr_weight); merge_num(no_path_retry); merge_num(minio); merge_num(minio_rq); merge_num(flush_on_last_del); merge_num(fast_io_fail); merge_num(dev_loss); merge_num(user_friendly_names); merge_num(retain_hwhandler); merge_num(detect_prio); merge_num(deferred_remove); merge_num(delay_watch_checks); merge_num(delay_wait_checks); /* * Make sure features is consistent with * no_path_retry */ if (dst->no_path_retry == NO_PATH_RETRY_FAIL) remove_feature(&dst->features, "queue_if_no_path"); else if (dst->no_path_retry != NO_PATH_RETRY_UNDEF) add_feature(&dst->features, "queue_if_no_path"); return 0; } int store_hwe (vector hwtable, struct hwentry * dhwe) { struct hwentry * hwe; if (find_hwe_strmatch(hwtable, dhwe)) return 0; if (!(hwe = alloc_hwe())) return 1; if (!dhwe->vendor || !(hwe->vendor = set_param_str(dhwe->vendor))) goto out; if (!dhwe->product || !(hwe->product = set_param_str(dhwe->product))) goto out; if (dhwe->revision && !(hwe->revision = set_param_str(dhwe->revision))) goto out; if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute))) goto out; if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid))) goto out; if (dhwe->features && !(hwe->features = set_param_str(dhwe->features))) goto out; if (dhwe->hwhandler && !(hwe->hwhandler = set_param_str(dhwe->hwhandler))) goto out; if (dhwe->selector && !(hwe->selector = set_param_str(dhwe->selector))) goto out; if (dhwe->checker_name && !(hwe->checker_name = set_param_str(dhwe->checker_name))) goto out; if (dhwe->prio_name && !(hwe->prio_name = set_param_str(dhwe->prio_name))) goto out; if (dhwe->prio_args && !(hwe->prio_args = set_param_str(dhwe->prio_args))) goto out; if (dhwe->alias_prefix && !(hwe->alias_prefix = set_param_str(dhwe->alias_prefix))) goto out; hwe->pgpolicy = dhwe->pgpolicy; hwe->pgfailback = dhwe->pgfailback; hwe->rr_weight = dhwe->rr_weight; hwe->no_path_retry = dhwe->no_path_retry; hwe->minio = dhwe->minio; hwe->minio_rq = dhwe->minio_rq; hwe->flush_on_last_del = dhwe->flush_on_last_del; hwe->fast_io_fail = dhwe->fast_io_fail; hwe->dev_loss = dhwe->dev_loss; hwe->user_friendly_names = dhwe->user_friendly_names; hwe->retain_hwhandler = dhwe->retain_hwhandler; hwe->detect_prio = dhwe->detect_prio; conf->deferred_remove = DEFAULT_DEFERRED_REMOVE; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; if (!vector_alloc_slot(hwtable)) goto out; vector_set_slot(hwtable, hwe); return 0; out: free_hwe(hwe); return 1; } static void factorize_hwtable (vector hw, int n) { struct hwentry *hwe1, *hwe2; int i, j; restart: vector_foreach_slot(hw, hwe1, i) { if (i == n) break; j = n; vector_foreach_slot_after(hw, hwe2, j) { if (hwe_regmatch(hwe1, hwe2)) continue; /* dup */ merge_hwe(hwe2, hwe1); if (hwe_strmatch(hwe2, hwe1) == 0) { vector_del_slot(hw, i); free_hwe(hwe1); n -= 1; /* * Play safe here; we have modified * the original vector so the outer * vector_foreach_slot() might * become confused. */ goto restart; } } } return; } struct config * alloc_config (void) { return (struct config *)MALLOC(sizeof(struct config)); } void free_config (struct config * conf) { if (!conf) return; if (conf->dev) FREE(conf->dev); if (conf->multipath_dir) FREE(conf->multipath_dir); if (conf->selector) FREE(conf->selector); if (conf->uid_attribute) FREE(conf->uid_attribute); if (conf->getuid) FREE(conf->getuid); if (conf->features) FREE(conf->features); if (conf->hwhandler) FREE(conf->hwhandler); if (conf->bindings_file) FREE(conf->bindings_file); if (conf->wwids_file) FREE(conf->wwids_file); if (conf->prio_name) FREE(conf->prio_name); if (conf->alias_prefix) FREE(conf->alias_prefix); if (conf->partition_delim) FREE(conf->partition_delim); if (conf->prio_args) FREE(conf->prio_args); if (conf->checker_name) FREE(conf->checker_name); if (conf->config_dir) FREE(conf->config_dir); if (conf->reservation_key) FREE(conf->reservation_key); free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); free_blacklist(conf->blist_property); free_blacklist_device(conf->blist_device); free_blacklist(conf->elist_devnode); free_blacklist(conf->elist_wwid); free_blacklist(conf->elist_property); free_blacklist_device(conf->elist_device); free_mptable(conf->mptable); free_hwtable(conf->hwtable); free_hwe(conf->overrides); free_keywords(conf->keywords); FREE(conf); } /* if multipath fails to process the config directory, it should continue, * with just a warning message */ static void process_config_dir(vector keywords, char *dir) { struct dirent **namelist; int i, n; char path[LINE_MAX]; int old_hwtable_size; if (dir[0] != '/') { condlog(1, "config_dir '%s' must be a fully qualified path", dir); return; } n = scandir(dir, &namelist, NULL, alphasort); if (n < 0) { if (errno == ENOENT) condlog(3, "No configuration dir '%s'", dir); else condlog(0, "couldn't open configuration dir '%s': %s", dir, strerror(errno)); return; } for (i = 0; i < n; i++) { if (!strstr(namelist[i]->d_name, ".conf")) continue; old_hwtable_size = VECTOR_SIZE(conf->hwtable); snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); path[LINE_MAX-1] = '\0'; process_file(path); if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size) factorize_hwtable(conf->hwtable, old_hwtable_size); } } int load_config (char * file, struct udev *udev) { if (!conf) conf = alloc_config(); if (!conf || !udev) return 1; /* * internal defaults */ if (!conf->verbosity) conf->verbosity = DEFAULT_VERBOSITY; conf->udev = udev; conf->dev_type = DEV_NONE; conf->minio = DEFAULT_MINIO; conf->minio_rq = DEFAULT_MINIO_RQ; get_sys_max_fds(&conf->max_fds); conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); conf->wwids_file = set_default(DEFAULT_WWIDS_FILE); conf->bindings_read_only = 0; conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR); conf->features = set_default(DEFAULT_FEATURES); conf->flush_on_last_del = 0; conf->attribute_flags = 0; conf->reassign_maps = DEFAULT_REASSIGN_MAPS; conf->checkint = DEFAULT_CHECKINT; conf->max_checkint = 0; conf->pgfailback = DEFAULT_FAILBACK; conf->fast_io_fail = DEFAULT_FAST_IO_FAIL; conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER; conf->detect_prio = DEFAULT_DETECT_PRIO; conf->force_sync = 0; conf->partition_delim = NULL; conf->processed_main_config = 0; conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; conf->uxsock_timeout = DEFAULT_UXSOCK_TIMEOUT; conf->uid_attribute = set_default(DEFAULT_UID_ATTRIBUTE); /* * preload default hwtable */ if (conf->hwtable == NULL) { conf->hwtable = vector_alloc(); if (!conf->hwtable) goto out; } if (setup_default_hwtable(conf->hwtable)) goto out; /* * read the config file */ set_current_keywords(&conf->keywords); alloc_keywords(); init_keywords(); if (filepresent(file)) { int builtin_hwtable_size; builtin_hwtable_size = VECTOR_SIZE(conf->hwtable); if (process_file(file)) { condlog(0, "error parsing config file"); goto out; } if (VECTOR_SIZE(conf->hwtable) > builtin_hwtable_size) { /* * remove duplica in hwtable. config file * takes precedence over build-in hwtable */ factorize_hwtable(conf->hwtable, builtin_hwtable_size); } } conf->processed_main_config = 1; if (conf->config_dir == NULL) conf->config_dir = set_default(DEFAULT_CONFIG_DIR); if (conf->config_dir && conf->config_dir[0] != '\0') process_config_dir(conf->keywords, conf->config_dir); /* * fill the voids left in the config file */ if (conf->max_checkint == 0) conf->max_checkint = MAX_CHECKINT(conf->checkint); if (conf->blist_devnode == NULL) { conf->blist_devnode = vector_alloc(); if (!conf->blist_devnode) goto out; } if (conf->blist_wwid == NULL) { conf->blist_wwid = vector_alloc(); if (!conf->blist_wwid) goto out; } if (conf->blist_device == NULL) { conf->blist_device = vector_alloc(); if (!conf->blist_device) goto out; } if (conf->blist_property == NULL) { conf->blist_property = vector_alloc(); if (!conf->blist_property) goto out; } if (conf->elist_devnode == NULL) { conf->elist_devnode = vector_alloc(); if (!conf->elist_devnode) goto out; } if (conf->elist_wwid == NULL) { conf->elist_wwid = vector_alloc(); if (!conf->elist_wwid) goto out; } if (conf->elist_device == NULL) { conf->elist_device = vector_alloc(); if (!conf->elist_device) goto out; } if (conf->elist_property == NULL) { conf->elist_property = vector_alloc(); if (!conf->elist_property) goto out; } if (setup_default_blist(conf)) goto out; if (conf->mptable == NULL) { conf->mptable = vector_alloc(); if (!conf->mptable) goto out; } if (conf->bindings_file == NULL) conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); if (!conf->multipath_dir || !conf->bindings_file || !conf->wwids_file) goto out; return 0; out: free_config(conf); return 1; } multipath-tools-0.5.0+git1.656f8865/libmultipath/config.h000066400000000000000000000071651260771327300226760ustar00rootroot00000000000000#ifndef _CONFIG_H #define _CONFIG_H #include #include #define ORIGIN_DEFAULT 0 #define ORIGIN_CONFIG 1 /* * In kernel, fast_io_fail == 0 means immediate failure on rport delete. * OTOH '0' means not-configured in various places in multipath-tools. */ #define MP_FAST_IO_FAIL_UNSET (0) #define MP_FAST_IO_FAIL_OFF (-1) #define MP_FAST_IO_FAIL_ZERO (-2) enum devtypes { DEV_NONE, DEV_DEVT, DEV_DEVNODE, DEV_DEVMAP, DEV_UEVENT }; enum mpath_cmds { CMD_CREATE, CMD_DRY_RUN, CMD_LIST_SHORT, CMD_LIST_LONG, CMD_VALID_PATH, CMD_REMOVE_WWID, CMD_RESET_WWIDS, CMD_ADD_WWID, }; struct hwentry { char * vendor; char * product; char * revision; char * uid_attribute; char * getuid; char * features; char * hwhandler; char * selector; char * checker_name; char * prio_name; char * prio_args; char * alias_prefix; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int minio_rq; int flush_on_last_del; int fast_io_fail; unsigned int dev_loss; int user_friendly_names; int retain_hwhandler; int detect_prio; int deferred_remove; int delay_watch_checks; int delay_wait_checks; char * bl_product; }; struct mpentry { char * wwid; char * alias; char * uid_attribute; char * getuid; char * selector; char * features; char * prio_name; char * prio_args; unsigned char * reservation_key; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int minio_rq; int flush_on_last_del; int attribute_flags; int user_friendly_names; int deferred_remove; int delay_watch_checks; int delay_wait_checks; uid_t uid; gid_t gid; mode_t mode; }; struct config { int verbosity; enum mpath_cmds cmd; int pgpolicy_flag; int pgpolicy; enum devtypes dev_type; int minio; int minio_rq; int checkint; int max_checkint; int pgfailback; int remove; int rr_weight; int no_path_retry; int user_friendly_names; int bindings_read_only; int max_fds; int force_reload; int queue_without_daemon; int ignore_wwids; int checker_timeout; int daemon; int flush_on_last_del; int attribute_flags; int fast_io_fail; unsigned int dev_loss; int log_checker_err; int allow_queueing; int find_multipaths; uid_t uid; gid_t gid; mode_t mode; int reassign_maps; int retain_hwhandler; int detect_prio; int force_sync; int deferred_remove; int processed_main_config; int delay_watch_checks; int delay_wait_checks; int uxsock_timeout; unsigned int version[3]; char * dev; struct udev * udev; char * multipath_dir; char * selector; char * uid_attribute; char * getuid; char * features; char * hwhandler; char * bindings_file; char * wwids_file; char * prio_name; char * prio_args; char * checker_name; char * alias_prefix; char * partition_delim; char * config_dir; unsigned char * reservation_key; vector keywords; vector mptable; vector hwtable; struct hwentry *overrides; vector blist_devnode; vector blist_wwid; vector blist_device; vector blist_property; vector elist_devnode; vector elist_wwid; vector elist_device; vector elist_property; }; struct config * conf; struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char *revision); struct mpentry * find_mpe (char * wwid); char * get_mpe_wwid (char * alias); struct hwentry * alloc_hwe (void); struct mpentry * alloc_mpe (void); void free_hwe (struct hwentry * hwe); void free_hwtable (vector hwtable); void free_mpe (struct mpentry * mpe); void free_mptable (vector mptable); int store_hwe (vector hwtable, struct hwentry *); int load_config (char * file, struct udev * udev); struct config * alloc_config (void); void free_config (struct config * conf); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/configure.c000066400000000000000000000573641260771327300234130ustar00rootroot00000000000000/* * Copyright (c) 2003, 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "devmapper.h" #include "defaults.h" #include "structs.h" #include "structs_vec.h" #include "dmparser.h" #include "config.h" #include "blacklist.h" #include "propsel.h" #include "discovery.h" #include "debug.h" #include "switchgroup.h" #include "print.h" #include "configure.h" #include "pgpolicies.h" #include "dict.h" #include "alias.h" #include "prio.h" #include "util.h" #include "uxsock.h" #include "wwids.h" /* group paths in pg by host adapter */ int group_by_host_adapter(struct pathgroup *pgp, vector adapters) { struct adapter_group *agp; struct host_group *hgp; struct path *pp, *pp1; char adapter_name1[SLOT_NAME_SIZE]; char adapter_name2[SLOT_NAME_SIZE]; int i, j; int found_hostgroup = 0; while (VECTOR_SIZE(pgp->paths) > 0) { pp = VECTOR_SLOT(pgp->paths, 0); if (sysfs_get_host_adapter_name(pp, adapter_name1)) goto out; /* create a new host adapter group */ agp = alloc_adaptergroup(); if (!agp) goto out; agp->pgp = pgp; strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE); store_adaptergroup(adapters, agp); /* create a new host port group */ hgp = alloc_hostgroup(); if (!hgp) goto out; if (store_hostgroup(agp->host_groups, hgp)) goto out; hgp->host_no = pp->sg_id.host_no; agp->num_hosts++; if (store_path(hgp->paths, pp)) goto out; hgp->num_paths++; /* delete path from path group */ vector_del_slot(pgp->paths, 0); /* add all paths belonging to same host adapter */ vector_foreach_slot(pgp->paths, pp1, i) { if (sysfs_get_host_adapter_name(pp1, adapter_name2)) goto out; if (strcmp(adapter_name1, adapter_name2) == 0) { found_hostgroup = 0; vector_foreach_slot(agp->host_groups, hgp, j) { if (hgp->host_no == pp1->sg_id.host_no) { if (store_path(hgp->paths, pp1)) goto out; hgp->num_paths++; found_hostgroup = 1; break; } } if (!found_hostgroup) { /* this path belongs to new host port * within this adapter */ hgp = alloc_hostgroup(); if (!hgp) goto out; if (store_hostgroup(agp->host_groups, hgp)) goto out; agp->num_hosts++; if (store_path(hgp->paths, pp1)) goto out; hgp->host_no = pp1->sg_id.host_no; hgp->num_paths++; } /* delete paths from original path_group * as they are added into adapter group now */ vector_del_slot(pgp->paths, i); i--; } } } return 0; out: /* add back paths into pg as re-ordering failed */ vector_foreach_slot(adapters, agp, i) { vector_foreach_slot(agp->host_groups, hgp, j) { while (VECTOR_SIZE(hgp->paths) > 0) { pp = VECTOR_SLOT(hgp->paths, 0); if (store_path(pgp->paths, pp)) condlog(3, "failed to restore " "path %s into path group", pp->dev); vector_del_slot(hgp->paths, 0); } } } free_adaptergroup(adapters); return 1; } /* re-order paths in pg by alternating adapters and host ports * for optimized selection */ int order_paths_in_pg_by_alt_adapters(struct pathgroup *pgp, vector adapters, int total_paths) { int next_adapter_index = 0; struct adapter_group *agp; struct host_group *hgp; struct path *pp; while (total_paths > 0) { agp = VECTOR_SLOT(adapters, next_adapter_index); if (!agp) { condlog(0, "can't get adapter group %d", next_adapter_index); return 1; } hgp = VECTOR_SLOT(agp->host_groups, agp->next_host_index); if (!hgp) { condlog(0, "can't get host group %d of adapter group %d", next_adapter_index, agp->next_host_index); return 1; } if (!hgp->num_paths) { agp->next_host_index++; agp->next_host_index %= agp->num_hosts; next_adapter_index++; next_adapter_index %= VECTOR_SIZE(adapters); continue; } pp = VECTOR_SLOT(hgp->paths, 0); if (store_path(pgp->paths, pp)) return 1; total_paths--; vector_del_slot(hgp->paths, 0); hgp->num_paths--; agp->next_host_index++; agp->next_host_index %= agp->num_hosts; next_adapter_index++; next_adapter_index %= VECTOR_SIZE(adapters); } /* all paths are added into path_group * in crafted child order */ return 0; } /* round-robin: order paths in path group to alternate * between all host adapters */ int rr_optimize_path_order(struct pathgroup *pgp) { vector adapters; struct path *pp; int total_paths; int i; total_paths = VECTOR_SIZE(pgp->paths); vector_foreach_slot(pgp->paths, pp, i) { if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && pp->sg_id.proto_id != SCSI_PROTOCOL_SAS && pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) { /* return success as default path order * is maintained in path group */ return 0; } } adapters = vector_alloc(); if (!adapters) return 0; /* group paths in path group by host adapters */ if (group_by_host_adapter(pgp, adapters)) { /* already freed adapters */ condlog(3, "Failed to group paths by adapters"); return 0; } /* re-order paths in pg to alternate between adapters and host ports */ if (order_paths_in_pg_by_alt_adapters(pgp, adapters, total_paths)) { condlog(3, "Failed to re-order paths in pg by adapters " "and host ports"); free_adaptergroup(adapters); /* return failure as original paths are * removed form pgp */ return 1; } free_adaptergroup(adapters); return 0; } extern int setup_map (struct multipath * mpp, char * params, int params_size) { struct pathgroup * pgp; int i; /* * don't bother if devmap size is unknown */ if (mpp->size <= 0) { condlog(3, "%s: devmap size is unknown", mpp->alias); return 1; } /* * free features, selector, and hwhandler properties if they are being reused */ free_multipath_attributes(mpp); /* * properties selectors */ select_pgfailback(mpp); select_pgpolicy(mpp); select_selector(mpp); select_features(mpp); select_hwhandler(mpp); select_rr_weight(mpp); select_minio(mpp); select_no_path_retry(mpp); select_mode(mpp); select_uid(mpp); select_gid(mpp); select_fast_io_fail(mpp); select_dev_loss(mpp); select_reservation_key(mpp); select_retain_hwhandler(mpp); select_deferred_remove(mpp); select_delay_watch_checks(mpp); select_delay_wait_checks(mpp); sysfs_set_scsi_tmo(mpp); /* * assign paths to path groups -- start with no groups and all paths * in mpp->paths */ if (mpp->pg) { vector_foreach_slot (mpp->pg, pgp, i) free_pathgroup(pgp, KEEP_PATHS); vector_free(mpp->pg); mpp->pg = NULL; } if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp)) return 1; mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); /* * ponders each path group and determine highest prio pg * to switch over (default to first) */ mpp->bestpg = select_path_group(mpp); /* re-order paths in all path groups in an optimized way * for round-robin path selectors to get maximum throughput. */ if (!strncmp(mpp->selector, "round-robin", 11)) { vector_foreach_slot(mpp->pg, pgp, i) { if (VECTOR_SIZE(pgp->paths) <= 2) continue; if (rr_optimize_path_order(pgp)) { condlog(2, "cannot re-order paths for " "optimization: %s", mpp->alias); return 1; } } } /* * transform the mp->pg vector of vectors of paths * into a mp->params strings to feed the device-mapper */ if (assemble_map(mpp, params, params_size)) { condlog(0, "%s: problem assembing map", mpp->alias); return 1; } return 0; } static void compute_pgid(struct pathgroup * pgp) { struct path * pp; int i; vector_foreach_slot (pgp->paths, pp, i) pgp->id ^= (long)pp; } static int pgcmp (struct multipath * mpp, struct multipath * cmpp) { int i, j; struct pathgroup * pgp; struct pathgroup * cpgp; int r = 0; if (!mpp) return 0; vector_foreach_slot (mpp->pg, pgp, i) { compute_pgid(pgp); vector_foreach_slot (cmpp->pg, cpgp, j) { if (pgp->id == cpgp->id && !pathcmp(pgp, cpgp)) { r = 0; break; } r++; } if (r) return r; } return r; } static void select_action (struct multipath * mpp, vector curmp, int force_reload) { struct multipath * cmpp; struct multipath * cmpp_by_name; cmpp = find_mp_by_wwid(curmp, mpp->wwid); cmpp_by_name = find_mp_by_alias(curmp, mpp->alias); if (!cmpp_by_name) { if (cmpp) { condlog(2, "%s: rename %s to %s", mpp->wwid, cmpp->alias, mpp->alias); strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE); mpp->action = ACT_RENAME; if (force_reload) mpp->action = ACT_RENAME2; return; } mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map does not exist)", mpp->alias); return; } if (!cmpp) { condlog(2, "%s: remove (wwid changed)", mpp->alias); dm_flush_map(mpp->alias); strncpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE); drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS); mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map wwid change)", mpp->alias); return; } if (cmpp != cmpp_by_name) { condlog(2, "%s: unable to rename %s to %s (%s is used by %s)", mpp->wwid, cmpp->alias, mpp->alias, mpp->alias, cmpp_by_name->wwid); mpp->action = ACT_NOTHING; return; } if (pathcount(mpp, PATH_UP) == 0) { mpp->action = ACT_NOTHING; condlog(3, "%s: set ACT_NOTHING (no usable path)", mpp->alias); return; } if (force_reload) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (forced by user)", mpp->alias); return; } if (cmpp->size != mpp->size) { mpp->action = ACT_RESIZE; condlog(3, "%s: set ACT_RESIZE (size change)", mpp->alias); return; } if (!mpp->no_path_retry && (strlen(cmpp->features) != strlen(mpp->features) || strcmp(cmpp->features, mpp->features))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (features change)", mpp->alias); return; } if (mpp->retain_hwhandler != RETAIN_HWHANDLER_ON && (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) || strncmp(cmpp->hwhandler, mpp->hwhandler, strlen(mpp->hwhandler)))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (hwhandler change)", mpp->alias); return; } if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector, strlen(mpp->selector))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (selector change)", mpp->alias); return; } if (cmpp->minio != mpp->minio) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (minio change, %u->%u)", mpp->alias, cmpp->minio, mpp->minio); return; } if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (path group number change)", mpp->alias); return; } if (pgcmp(mpp, cmpp)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (path group topology change)", mpp->alias); return; } if (cmpp->nextpg != mpp->bestpg) { mpp->action = ACT_SWITCHPG; condlog(3, "%s: set ACT_SWITCHPG (next path group change)", mpp->alias); return; } mpp->action = ACT_NOTHING; condlog(3, "%s: set ACT_NOTHING (map unchanged)", mpp->alias); return; } extern int reinstate_paths (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) { if (pp->state != PATH_UP && (pgp->status == PGSTATE_DISABLED || pgp->status == PGSTATE_ACTIVE)) continue; if (pp->dmstate == PSTATE_FAILED) { if (dm_reinstate_path(mpp->alias, pp->dev_t)) condlog(0, "%s: error reinstating", pp->dev); } } } return 0; } static int lock_multipath (struct multipath * mpp, int lock) { struct pathgroup * pgp; struct path * pp; int i, j; int x, y; if (!mpp || !mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot(pgp->paths, pp, j) { if (lock && flock(pp->fd, LOCK_EX | LOCK_NB) && errno == EWOULDBLOCK) goto fail; else if (!lock) flock(pp->fd, LOCK_UN); } } return 0; fail: vector_foreach_slot (mpp->pg, pgp, x) { if (x > i) return 1; if (!pgp->paths) continue; vector_foreach_slot(pgp->paths, pp, y) { if (x == i && y >= j) return 1; flock(pp->fd, LOCK_UN); } } return 1; } /* * Return value: */ #define DOMAP_RETRY -1 #define DOMAP_FAIL 0 #define DOMAP_OK 1 #define DOMAP_EXIST 2 #define DOMAP_DRY 3 extern int domap (struct multipath * mpp, char * params) { int r = DOMAP_FAIL; /* * last chance to quit before touching the devmaps */ if (conf->cmd == CMD_DRY_RUN && mpp->action != ACT_NOTHING) { print_multipath_topology(mpp, conf->verbosity); return DOMAP_DRY; } if (mpp->action == ACT_CREATE && dm_map_present(mpp->alias)) { condlog(3, "%s: map already present", mpp->alias); mpp->action = ACT_RELOAD; } switch (mpp->action) { case ACT_REJECT: case ACT_NOTHING: return DOMAP_EXIST; case ACT_SWITCHPG: dm_switchgroup(mpp->alias, mpp->bestpg); /* * we may have avoided reinstating paths because there where in * active or disabled PG. Now that the topology has changed, * retry. */ reinstate_paths(mpp); return DOMAP_EXIST; case ACT_CREATE: if (lock_multipath(mpp, 1)) { condlog(3, "%s: failed to create map (in use)", mpp->alias); return DOMAP_RETRY; } r = dm_addmap_create(mpp, params); lock_multipath(mpp, 0); break; case ACT_RELOAD: r = dm_addmap_reload(mpp, params); if (r) r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, 0, MPATH_UDEV_RELOAD_FLAG); break; case ACT_RESIZE: r = dm_addmap_reload(mpp, params); if (r) r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1, 0); break; case ACT_RENAME: r = dm_rename(mpp->alias_old, mpp->alias); break; case ACT_RENAME2: r = dm_rename(mpp->alias_old, mpp->alias); if (r) { r = dm_addmap_reload(mpp, params); if (r) r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, 0, MPATH_UDEV_RELOAD_FLAG); } break; default: break; } if (r == DOMAP_OK) { /* * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ if (mpp->action == ACT_CREATE) remember_wwid(mpp->wwid); if (!conf->daemon) { /* multipath client mode */ dm_switchgroup(mpp->alias, mpp->bestpg); } else { /* multipath daemon mode */ mpp->stat_map_loads++; condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, TGT_MPATH, params); /* * Required action is over, reset for the stateful daemon. * But don't do it for creation as we use in the caller the * mpp->action to figure out whether to start the watievent checker. */ if (mpp->action != ACT_CREATE) mpp->action = ACT_NOTHING; } dm_setgeometry(mpp); return DOMAP_OK; } return DOMAP_FAIL; } static int deadmap (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 1; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) if (strlen(pp->dev)) return 0; /* alive */ } return 1; /* dead */ } int check_daemon(void) { int fd; char *reply; size_t len; int ret = 0; fd = ux_socket_connect(DEFAULT_SOCKET); if (fd == -1) return 0; if (send_packet(fd, "show daemon", 12) != 0) goto out; if (recv_packet(fd, &reply, &len, conf->uxsock_timeout) != 0) goto out; if (strstr(reply, "shutdown")) goto out_free; ret = 1; out_free: FREE(reply); out: close(fd); return ret; } extern int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_reload) { int r = 1; int k, i; char params[PARAMS_SIZE]; struct multipath * mpp; struct path * pp1; struct path * pp2; vector curmp = vecs->mpvec; vector pathvec = vecs->pathvec; /* ignore refwwid if it's empty */ if (refwwid && !strlen(refwwid)) refwwid = NULL; if (force_reload) { vector_foreach_slot (pathvec, pp1, k) { pp1->mpp = NULL; } } vector_foreach_slot (pathvec, pp1, k) { /* skip this path for some reason */ /* 1. if path has no unique id or wwid blacklisted */ if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0) { orphan_path(pp1, "wwid blacklisted"); continue; } /* 2. if path already coalesced */ if (pp1->mpp) continue; /* 3. if path has disappeared */ if (pp1->state == PATH_REMOVED) { orphan_path(pp1, "path removed"); continue; } /* 4. path is out of scope */ if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE)) continue; /* If find_multipaths was selected check if the path is valid */ if (conf->find_multipaths && !refwwid && !should_multipath(pp1, pathvec)) { orphan_path(pp1, "only one path"); continue; } /* * at this point, we know we really got a new mp */ mpp = add_map_with_path(vecs, pp1, 0); if (!mpp) return 1; if (pp1->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; if (!mpp->paths) { condlog(0, "%s: skip coalesce (no paths)", mpp->alias); remove_map(mpp, vecs, 0); continue; } for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) { pp2 = VECTOR_SLOT(pathvec, i); if (strcmp(pp1->wwid, pp2->wwid)) continue; if (!mpp->size && pp2->size) mpp->size = pp2->size; if (mpp->size && pp2->size && pp2->size != mpp->size) { /* * ouch, avoid feeding that to the DM */ condlog(0, "%s: size %llu, expected %llu. " "Discard", pp2->dev_t, pp2->size, mpp->size); mpp->action = ACT_REJECT; } if (pp2->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; } verify_paths(mpp, vecs); params[0] = '\0'; if (setup_map(mpp, params, PARAMS_SIZE)) { remove_map(mpp, vecs, 0); continue; } if (mpp->action == ACT_UNDEF) select_action(mpp, curmp, force_reload); r = domap(mpp, params); if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for create/reload map", mpp->alias, r); if (r == DOMAP_FAIL) { condlog(2, "%s: %s map", mpp->alias, (mpp->action == ACT_CREATE)? "ignoring" : "removing"); remove_map(mpp, vecs, 0); continue; } else /* if (r == DOMAP_RETRY) */ return r; } if (r == DOMAP_DRY) continue; if (!conf->daemon && !conf->allow_queueing && !check_daemon()) { if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) condlog(3, "%s: multipathd not running, unset " "queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 0)) remove_feature(&mpp->features, "queue_if_no_path"); } else if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) { condlog(3, "%s: unset queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 0)) remove_feature(&mpp->features, "queue_if_no_path"); } else { condlog(3, "%s: set queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 1)) add_feature(&mpp->features, "queue_if_no_path"); } } if (!conf->daemon && mpp->action != ACT_NOTHING) print_multipath_topology(mpp, conf->verbosity); if (newmp) { if (mpp->action != ACT_REJECT) { if (!vector_alloc_slot(newmp)) return 1; vector_set_slot(newmp, mpp); } else remove_map(mpp, vecs, 0); } } /* * Flush maps with only dead paths (ie not in sysfs) * Keep maps with only failed paths */ if (newmp) { vector_foreach_slot (newmp, mpp, i) { char alias[WWID_SIZE]; int j; if (!deadmap(mpp)) continue; strncpy(alias, mpp->alias, WWID_SIZE); if ((j = find_slot(newmp, (void *)mpp)) != -1) vector_del_slot(newmp, j); remove_map(mpp, vecs, 0); if (dm_flush_map(alias)) condlog(2, "%s: remove failed (dead)", alias); else condlog(2, "%s: remove (dead)", alias); } } return 0; } /* * returns: * 0 - success * 1 - failure * 2 - blacklist */ extern int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid) { int ret = 1; struct path * pp; char buff[FILE_NAME_SIZE]; char * refwwid = NULL, tmpwwid[WWID_SIZE]; if (!wwid) return 1; *wwid = NULL; if (dev_type == DEV_NONE) return 1; if (dev_type == DEV_DEVNODE) { if (basenamecpy(dev, buff, FILE_NAME_SIZE) == 0) { condlog(1, "basename failed for '%s' (%s)", dev, buff); return 1; } pp = find_path_by_dev(pathvec, buff); if (!pp) { struct udev_device *udevice = udev_device_new_from_subsystem_sysname(conf->udev, "block", buff); if (!udevice) { condlog(2, "%s: can't get udev device", buff); return 1; } ret = store_pathinfo(pathvec, conf->hwtable, udevice, DI_SYSFS | DI_WWID, &pp); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s: can't store path info", dev); return ret; } } if (pp->udev && filter_property(conf, pp->udev) > 0) return 2; refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVT) { strchop(dev); pp = find_path_by_devt(pathvec, dev); if (!pp) { struct udev_device *udevice = udev_device_new_from_devnum(conf->udev, 'b', parse_devt(dev)); if (!udevice) { condlog(2, "%s: can't get udev device", dev); return 1; } ret = store_pathinfo(pathvec, conf->hwtable, udevice, DI_SYSFS | DI_WWID, &pp); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s can't store path info", buff); return ret; } } if (pp->udev && filter_property(conf, pp->udev) > 0) return 2; refwwid = pp->wwid; goto out; } if (dev_type == DEV_UEVENT) { struct udev_device *udevice = udev_device_new_from_environment(conf->udev); if (!udevice) { condlog(2, "%s: can't get udev device", dev); return 1; } ret = store_pathinfo(pathvec, conf->hwtable, udevice, DI_SYSFS | DI_WWID, &pp); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s: can't store path info", dev); return ret; } if (pp->udev && filter_property(conf, pp->udev) > 0) return 2; refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVMAP) { if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { refwwid = tmpwwid; goto check; } /* * may be a binding */ if (get_user_friendly_wwid(dev, tmpwwid, conf->bindings_file) == 0) { refwwid = tmpwwid; goto check; } /* * or may be an alias */ refwwid = get_mpe_wwid(dev); /* * or directly a wwid */ if (!refwwid) refwwid = dev; check: if (refwwid && strlen(refwwid)) { if (filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid, NULL) > 0) return 2; } } out: if (refwwid && strlen(refwwid)) { *wwid = STRDUP(refwwid); return 0; } return 1; } extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh) { char params[PARAMS_SIZE] = {0}; struct path *pp; int i, r; update_mpp_paths(mpp, vecs->pathvec); if (refresh) { vector_foreach_slot (mpp->paths, pp, i) { r = pathinfo(pp, conf->hwtable, DI_PRIO); if (r) { condlog(2, "%s: failed to refresh pathinfo", mpp->alias); return 1; } } } if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map", mpp->alias); return 1; } select_action(mpp, vecs->mpvec, 1); r = domap(mpp, params); if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for reload map", mpp->alias, r); return 1; } if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) dm_queue_if_no_path(mpp->alias, 0); else dm_queue_if_no_path(mpp->alias, 1); } return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/configure.h000066400000000000000000000017371260771327300234110ustar00rootroot00000000000000/* * configurator actions */ #define ACT_NOTHING_STR "unchanged" #define ACT_REJECT_STR "reject" #define ACT_RELOAD_STR "reload" #define ACT_SWITCHPG_STR "switchpg" #define ACT_RENAME_STR "rename" #define ACT_CREATE_STR "create" #define ACT_RESIZE_STR "resize" enum actions { ACT_UNDEF, ACT_NOTHING, ACT_REJECT, ACT_RELOAD, ACT_SWITCHPG, ACT_RENAME, ACT_CREATE, ACT_RESIZE, ACT_RENAME2, }; #define FLUSH_ONE 1 #define FLUSH_ALL 2 int setup_map (struct multipath * mpp, char * params, int params_size ); int domap (struct multipath * mpp, char * params); int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload); int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid); int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh); int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name); multipath-tools-0.5.0+git1.656f8865/libmultipath/debug.c000066400000000000000000000013301260771327300224760ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include "log_pthread.h" #include #include #include "vector.h" #include "config.h" void dlog (int sink, int prio, const char * fmt, ...) { va_list ap; int thres; va_start(ap, fmt); thres = (conf) ? conf->verbosity : 0; if (prio <= thres) { if (sink < 1) { if (sink == 0) { time_t t = time(NULL); struct tm *tb = localtime(&t); char buff[16]; strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; fprintf(stdout, "%s | ", buff); } vfprintf(stdout, fmt, ap); } else log_safe(prio + 3, fmt, ap); } va_end(ap); } multipath-tools-0.5.0+git1.656f8865/libmultipath/debug.h000066400000000000000000000004061260771327300225060ustar00rootroot00000000000000void dlog (int sink, int prio, const char * fmt, ...) __attribute__((format(printf, 3, 4))); #include #include #include "log_pthread.h" extern int logsink; #define condlog(prio, fmt, args...) \ dlog(logsink, prio, fmt "\n", ##args) multipath-tools-0.5.0+git1.656f8865/libmultipath/defaults.c000066400000000000000000000003731260771327300232250ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include "memory.h" char * set_default (char * str) { int len; char * p; len = strlen(str); p = MALLOC(len + 1); if (!p) return NULL; strncat(p, str, len); return p; } multipath-tools-0.5.0+git1.656f8865/libmultipath/defaults.h000066400000000000000000000025151260771327300232320ustar00rootroot00000000000000#define DEFAULT_UID_ATTRIBUTE "ID_SERIAL" #define DEFAULT_UDEVDIR "/dev" #define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath" #define DEFAULT_SELECTOR "service-time 0" #define DEFAULT_ALIAS_PREFIX "mpath" #define DEFAULT_FEATURES "0" #define DEFAULT_HWHANDLER "0" #define DEFAULT_MINIO 1000 #define DEFAULT_MINIO_RQ 1 #define DEFAULT_PGPOLICY FAILOVER #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE #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_FAST_IO_FAIL 5 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF #define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF #define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF #define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF #define DEFAULT_UEVENT_STACKSIZE 256 #define DEFAULT_UXSOCK_TIMEOUT 1000 #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) #define MAX_DEV_LOSS_TMO 0x7FFFFFFF #define DEFAULT_PIDFILE "/var/run/multipathd.pid" #define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" char * set_default (char * str); multipath-tools-0.5.0+git1.656f8865/libmultipath/devmapper.c000066400000000000000000000707141260771327300234070ustar00rootroot00000000000000/* * snippets copied from device-mapper dmsetup.c * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat */ #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "memory.h" #include "devmapper.h" #include "config.h" #include "sysfs.h" #include "log_pthread.h" #include #include #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 #define UUID_PREFIX "mpath-" #define UUID_PREFIX_LEN 6 #ifdef LIBDM_API_DEFERRED static int dm_cancel_remove_partmaps(const char * mapname); #endif #ifndef LIBDM_API_COOKIE static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a) { return 1; } void dm_udev_wait(unsigned int c) { } void dm_udev_set_sync_support(int c) { } #endif static void dm_write_log (int level, const char *file, int line, const char *f, ...) { va_list ap; int thres; if (level > 6) level = 6; thres = (conf) ? conf->verbosity : 0; if (thres <= 3 || level > thres) return; va_start(ap, f); if (logsink < 1) { if (logsink == 0) { time_t t = time(NULL); struct tm *tb = localtime(&t); char buff[16]; strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; fprintf(stdout, "%s | ", buff); } fprintf(stdout, "libdevmapper: %s(%i): ", file, line); vfprintf(stdout, f, ap); fprintf(stdout, "\n"); } else { condlog(level, "libdevmapper: %s(%i): ", file, line); log_safe(level + 3, f, ap); } va_end(ap); return; } extern void dm_init(void) { dm_log_init(&dm_write_log); dm_log_init_verbose(conf ? conf->verbosity + 3 : 0); } static int dm_lib_prereq (void) { char version[64]; int v[3]; #if defined(LIBDM_API_DEFERRED) int minv[3] = {1, 2, 89}; #elif defined(DM_SUBSYSTEM_UDEV_FLAG0) int minv[3] = {1, 2, 82}; #elif defined(LIBDM_API_COOKIE) int minv[3] = {1, 2, 38}; #else int minv[3] = {1, 2, 8}; #endif dm_get_library_version(version, sizeof(version)); condlog(3, "libdevmapper version %s", version); sscanf(version, "%d.%d.%d ", &v[0], &v[1], &v[2]); if VERSION_GE(v, minv) return 0; condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d", minv[0], minv[1], minv[2]); return 1; } int dm_drv_version (unsigned int * version, char * str) { int r = 2; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; unsigned int *v; version[0] = 0; version[1] = 0; version[2] = 0; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) { condlog(0, "Can not communicate with kernel DM"); goto out; } target = dm_task_get_versions(dmt); do { last_target = target; if (!strncmp(str, target->name, strlen(str))) { r = 1; break; } target = (void *) target + target->next; } while (last_target != target); if (r == 2) { condlog(0, "DM %s kernel driver not loaded", str); goto out; } v = target->version; version[0] = v[0]; version[1] = v[1]; version[2] = v[2]; r = 0; out: dm_task_destroy(dmt); return r; } static int dm_drv_prereq (void) { 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)) { /* in doubt return not capable */ return 1; } /* test request based multipath capability */ condlog(3, "DM multipath kernel driver v%u.%u.%u", v[0], v[1], v[2]); if VERSION_GE(v, minv) return 0; condlog(0, "DM multipath kernel driver must be >= v%u.%u.%u", minv[0], minv[1], minv[2]); return 1; } extern int dm_prereq (void) { if (dm_lib_prereq()) return 1; return dm_drv_prereq(); } #define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS) static int dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) { int r = 0; int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE)); uint32_t cookie = 0; struct dm_task *dmt; if (!(dmt = dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, name)) goto out; dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); /* for DM_DEVICE_RESUME */ #ifdef LIBDM_API_FLUSH if (no_flush) dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ #endif #ifdef LIBDM_API_DEFERRED if (do_deferred(deferred_remove)) dm_task_deferred_remove(dmt); #endif if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, ((conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0) | udev_flags)) { dm_udev_complete(cookie); goto out; } r = dm_task_run (dmt); if (udev_wait_flag) { if (!r) dm_udev_complete(cookie); else dm_udev_wait(cookie); } out: dm_task_destroy (dmt); return r; } extern int dm_simplecmd_flush (int task, const char *name, int needsync, uint16_t udev_flags) { return dm_simplecmd(task, name, 0, needsync, udev_flags, 0); } extern int dm_simplecmd_noflush (int task, const char *name, int needsync, uint16_t udev_flags) { return dm_simplecmd(task, name, 1, needsync, udev_flags, 0); } static int dm_device_remove (const char *name, int needsync, int deferred_remove) { return dm_simplecmd(DM_DEVICE_REMOVE, name, 0, needsync, 0, deferred_remove); } extern int dm_addmap (int task, const char *target, struct multipath *mpp, char * params, int use_uuid, int ro) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; uint32_t cookie = 0; if (!(dmt = dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, mpp->alias)) goto addout; if (!dm_task_add_target (dmt, 0, mpp->size, target, params)) goto addout; if (ro) dm_task_set_ro(dmt); if (use_uuid && strlen(mpp->wwid) > 0){ prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1); if (!prefixed_uuid) { condlog(0, "cannot create prefixed uuid : %s", strerror(errno)); goto addout; } sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid); if (!dm_task_set_uuid(dmt, prefixed_uuid)) goto freeout; } if (mpp->attribute_flags & (1 << ATTR_MODE) && !dm_task_set_mode(dmt, mpp->mode)) goto freeout; if (mpp->attribute_flags & (1 << ATTR_UID) && !dm_task_set_uid(dmt, mpp->uid)) goto freeout; if (mpp->attribute_flags & (1 << ATTR_GID) && !dm_task_set_gid(dmt, mpp->gid)) goto freeout; condlog(4, "%s: addmap [0 %llu %s %s]", mpp->alias, mpp->size, target, params); dm_task_no_open_count(dmt); if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) { dm_udev_complete(cookie); goto freeout; } r = dm_task_run (dmt); if (task == DM_DEVICE_CREATE) { if (!r) dm_udev_complete(cookie); else dm_udev_wait(cookie); } freeout: if (prefixed_uuid) FREE(prefixed_uuid); addout: dm_task_destroy (dmt); return r; } extern int dm_addmap_create (struct multipath *mpp, char * params) { int ro; for (ro = 0; ro <= 1; ro++) { int err; if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro)) return 1; /* * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD. * Failing the second part leaves an empty map. Clean it up. */ err = errno; if (dm_map_present(mpp->alias)) { condlog(3, "%s: failed to load map (a path might be in use)", mpp->alias); dm_flush_map_nosync(mpp->alias); } if (err != EROFS) { condlog(3, "%s: failed to load map, error %d", mpp->alias, err); break; } } return 0; } #define ADDMAP_RW 0 #define ADDMAP_RO 1 extern int dm_addmap_reload (struct multipath *mpp, char *params) { if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW)) return 1; if (errno != EROFS) return 0; return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RO); } extern int dm_map_present (const char * str) { int r = 0; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, str)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (info.exists) r = 1; out: dm_task_destroy(dmt); return r; } extern int dm_get_map(const char * name, unsigned long long * size, char * outparams) { int r = 1; struct dm_task *dmt; void *next = NULL; uint64_t start, length; char *target_type = NULL; char *params = NULL; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 1; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); if (size) *size = length; if (!outparams) { r = 0; goto out; } if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_prefixed_uuid(const char *name, char *uuid) { struct dm_task *dmt; const char *uuidtmp; int r = 1; dmt = dm_task_create(DM_DEVICE_INFO); if (!dmt) return 1; if (!dm_task_set_name (dmt, name)) goto uuidout; if (!dm_task_run(dmt)) goto uuidout; uuidtmp = dm_task_get_uuid(dmt); if (uuidtmp) strcpy(uuid, uuidtmp); else uuid[0] = '\0'; r = 0; uuidout: dm_task_destroy(dmt); return r; } extern int dm_get_uuid(char *name, char *uuid) { char uuidtmp[WWID_SIZE]; if (dm_get_prefixed_uuid(name, uuidtmp)) return 1; if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN)) strcpy(uuid, uuidtmp + UUID_PREFIX_LEN); else strcpy(uuid, uuidtmp); return 0; } /* * returns: * 0 : if both uuids end with same suffix which starts with UUID_PREFIX * 1 : otherwise */ int dm_compare_uuid(const char* mapname1, const char* mapname2) { char *p1, *p2; char uuid1[WWID_SIZE], uuid2[WWID_SIZE]; if (dm_get_prefixed_uuid(mapname1, uuid1)) return 1; if (dm_get_prefixed_uuid(mapname2, uuid2)) return 1; p1 = strstr(uuid1, UUID_PREFIX); p2 = strstr(uuid2, UUID_PREFIX); if (p1 && p2 && !strcmp(p1, p2)) return 0; return 1; } extern int dm_get_status(char * name, char * outstatus) { int r = 1; struct dm_task *dmt; void *next = NULL; uint64_t start, length; char *target_type; char *status; if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) return 1; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ next = dm_get_next_target(dmt, next, &start, &length, &target_type, &status); if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE) r = 0; out: if (r) condlog(0, "%s: error getting map status string", name); dm_task_destroy(dmt); return r; } /* * returns: * 1 : match * 0 : no match * -1 : empty map */ extern int dm_type(const char * name, char * type) { int r = 0; struct dm_task *dmt; void *next = NULL; uint64_t start, length; char *target_type = NULL; char *params; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); if (!target_type) r = -1; else if (!strcmp(target_type, type)) r = 1; out: dm_task_destroy(dmt); return r; } extern int dm_is_mpath(const char * name) { int r = 0; struct dm_task *dmt; struct dm_info info; uint64_t start, length; char *target_type = NULL; char *params; const char *uuid; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info) || !info.exists) goto out; uuid = dm_task_get_uuid(dmt); if (!uuid || strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN) != 0) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); if (!target_type || strcmp(target_type, TGT_MPATH) != 0) goto out; r = 1; out: dm_task_destroy(dmt); return r; } static int dm_dev_t (const char * mapname, char * dev_t, int len) { int r = 1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info) || !info.exists) goto out; if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len) goto out; r = 0; out: dm_task_destroy(dmt); return r; } int dm_get_opencount (const char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.open_count; out: dm_task_destroy(dmt); return r; } int dm_get_major (char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.major; out: dm_task_destroy(dmt); return r; } int dm_get_minor (char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.minor; out: dm_task_destroy(dmt); return r; } extern int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove) { int r; if (!dm_is_mpath(mapname)) return 0; /* nothing to do */ if (dm_remove_partmaps(mapname, need_sync, deferred_remove)) return 1; if (!do_deferred(deferred_remove) && dm_get_opencount(mapname)) { condlog(2, "%s: map in use", mapname); return 1; } r = dm_device_remove(mapname, need_sync, deferred_remove); if (r) { if (do_deferred(deferred_remove) && dm_map_present(mapname)) { condlog(4, "multipath map %s remove deferred", mapname); return 2; } condlog(4, "multipath map %s removed", mapname); return 0; } return 1; } #ifdef LIBDM_API_DEFERRED int dm_flush_map_nopaths(const char * mapname, int deferred_remove) { return _dm_flush_map(mapname, 1, deferred_remove); } #else int dm_flush_map_nopaths(const char * mapname, int deferred_remove) { return _dm_flush_map(mapname, 1, 0); } #endif extern int dm_suspend_and_flush_map (const char * mapname) { int s = 0, queue_if_no_path = 0; unsigned long long mapsize; char params[PARAMS_SIZE] = {0}; if (!dm_is_mpath(mapname)) return 0; /* nothing to do */ if (!dm_get_map(mapname, &mapsize, params)) { if (strstr(params, "queue_if_no_path")) queue_if_no_path = 1; } if (queue_if_no_path) s = dm_queue_if_no_path((char *)mapname, 0); /* Leave queue_if_no_path alone if unset failed */ if (s) queue_if_no_path = 0; else s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 1, 0); if (!dm_flush_map(mapname)) { condlog(4, "multipath map %s removed", mapname); return 0; } condlog(2, "failed to remove multipath map %s", mapname); dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, 1, 0); if (queue_if_no_path) s = dm_queue_if_no_path((char *)mapname, 1); return 1; } extern int dm_flush_maps (void) { int r = 0; struct dm_task *dmt; struct dm_names *names; unsigned next = 0; if (!(dmt = dm_task_create (DM_DEVICE_LIST))) return 0; dm_task_no_open_count(dmt); if (!dm_task_run (dmt)) goto out; if (!(names = dm_task_get_names (dmt))) goto out; if (!names->dev) goto out; do { r |= dm_suspend_and_flush_map(names->name); next = names->next; names = (void *) names + next; } while (next); out: dm_task_destroy (dmt); return r; } int dm_message(char * mapname, char * message) { int r = 1; struct dm_task *dmt; if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG))) return 1; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_set_sector(dmt, 0)) goto out; if (!dm_task_set_message(dmt, message)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; r = 0; out: if (r) condlog(0, "DM message failed [%s]", message); dm_task_destroy(dmt); return r; } int dm_fail_path(char * mapname, char * path) { char message[32]; if (snprintf(message, 32, "fail_path %s", path) > 32) return 1; return dm_message(mapname, message); } int dm_reinstate_path(char * mapname, char * path) { char message[32]; if (snprintf(message, 32, "reinstate_path %s", path) > 32) return 1; return dm_message(mapname, message); } int dm_queue_if_no_path(char *mapname, int enable) { char *message; if (enable) message = "queue_if_no_path"; else message = "fail_if_no_path"; return dm_message(mapname, message); } static int dm_groupmsg (char * msg, char * mapname, int index) { char message[32]; if (snprintf(message, 32, "%s_group %i", msg, index) > 32) return 1; return dm_message(mapname, message); } int dm_switchgroup(char * mapname, int index) { return dm_groupmsg("switch", mapname, index); } int dm_enablegroup(char * mapname, int index) { return dm_groupmsg("enable", mapname, index); } int dm_disablegroup(char * mapname, int index) { return dm_groupmsg("disable", mapname, index); } int dm_get_maps (vector mp) { struct multipath * mpp; int r = 1; struct dm_task *dmt; struct dm_names *names; unsigned next = 0; if (!mp) return 1; if (!(dmt = dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } do { if (!dm_is_mpath(names->name)) goto next; mpp = alloc_multipath(); if (!mpp) goto out; mpp->alias = STRDUP(names->name); if (!mpp->alias) goto out1; if (dm_get_map(names->name, &mpp->size, NULL)) goto out1; dm_get_uuid(names->name, mpp->wwid); dm_get_info(names->name, &mpp->dmi); if (!vector_alloc_slot(mp)) goto out1; vector_set_slot(mp, mpp); mpp = NULL; next: next = names->next; names = (void *) names + next; } while (next); r = 0; goto out; out1: free_multipath(mpp, KEEP_PATHS); out: dm_task_destroy (dmt); return r; } int dm_geteventnr (char *name) { struct dm_task *dmt; struct dm_info info; int event = -1; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return -1; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (info.exists) event = info.event_nr; out: dm_task_destroy(dmt); return event; } char * dm_mapname(int major, int minor) { char * response = NULL; const char *map; struct dm_task *dmt; int r; int loop = MAX_WAIT * LOOPS_PER_SEC; if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) return NULL; if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) goto bad; dm_task_no_open_count(dmt); /* * device map might not be ready when we get here from * daemon uev_trigger -> uev_add_map */ while (--loop) { r = dm_task_run(dmt); if (r) break; usleep(1000 * 1000 / LOOPS_PER_SEC); } if (!r) { condlog(0, "%i:%i: timeout fetching map name", major, minor); goto bad; } map = dm_task_get_name(dmt); if (map && strlen(map)) response = STRDUP((char *)dm_task_get_name(dmt)); dm_task_destroy(dmt); return response; bad: dm_task_destroy(dmt); condlog(0, "%i:%i: error fetching map name", major, minor); return NULL; } static int do_foreach_partmaps (const char * mapname, int (*partmap_func)(char *, void *), void *data) { struct dm_task *dmt; struct dm_names *names; unsigned next = 0; char params[PARAMS_SIZE]; unsigned long long size; char dev_t[32]; int r = 1; if (!(dmt = dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } if (dm_dev_t(mapname, &dev_t[0], 32)) goto out; do { if ( /* * if devmap target is "linear" */ (dm_type(names->name, TGT_PART) > 0) && /* * and both uuid end with same suffix starting * at UUID_PREFIX */ (!dm_compare_uuid(names->name, mapname)) && /* * and we can fetch the map table from the kernel */ !dm_get_map(names->name, &size, ¶ms[0]) && /* * and the table maps over the multipath map */ strstr(params, dev_t) ) { if (partmap_func(names->name, data) != 0) goto out; } next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } struct remove_data { int need_sync; int deferred_remove; }; static int remove_partmap(char *name, void *data) { struct remove_data *rd = (struct remove_data *)data; if (dm_get_opencount(name)) { dm_remove_partmaps(name, rd->need_sync, rd->deferred_remove); if (!do_deferred(rd->deferred_remove) && dm_get_opencount(name)) { condlog(2, "%s: map in use", name); return 1; } } condlog(4, "partition map %s removed", name); dm_device_remove(name, rd->need_sync, rd->deferred_remove); return 0; } int dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove) { struct remove_data rd = { need_sync, deferred_remove }; return do_foreach_partmaps(mapname, remove_partmap, &rd); } #ifdef LIBDM_API_DEFERRED static int cancel_remove_partmap (char *name, void *unused) { if (dm_get_opencount(name)) dm_cancel_remove_partmaps(name); if (dm_message(name, "@cancel_deferred_remove") != 0) condlog(0, "%s: can't cancel deferred remove: %s", name, strerror(errno)); return 0; } static int dm_get_deferred_remove (char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return -1; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; r = info.deferred_remove; out: dm_task_destroy(dmt); return r; } static int dm_cancel_remove_partmaps(const char * mapname) { return do_foreach_partmaps(mapname, cancel_remove_partmap, NULL); } int dm_cancel_deferred_remove (struct multipath *mpp) { int r = 0; if (!dm_get_deferred_remove(mpp->alias)) return 0; if (mpp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) mpp->deferred_remove = DEFERRED_REMOVE_ON; dm_cancel_remove_partmaps(mpp->alias); r = dm_message(mpp->alias, "@cancel_deferred_remove"); if (r) condlog(0, "%s: can't cancel deferred remove: %s", mpp->alias, strerror(errno)); else condlog(2, "%s: canceled deferred remove", mpp->alias); return r; } #else int dm_cancel_deferred_remove (struct multipath *mpp) { return 0; } #endif static struct dm_info * alloc_dminfo (void) { return MALLOC(sizeof(struct dm_info)); } int dm_get_info (char * mapname, struct dm_info ** dmi) { int r = 1; struct dm_task *dmt = NULL; if (!mapname) return 1; if (!*dmi) *dmi = alloc_dminfo(); if (!*dmi) return 1; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) goto out; if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, *dmi)) goto out; r = 0; out: if (r) { memset(*dmi, 0, sizeof(struct dm_info)); FREE(*dmi); *dmi = NULL; } if (dmt) dm_task_destroy(dmt); return r; } struct rename_data { char *old; char *new; char *delim; }; static int rename_partmap (char *name, void *data) { char buff[PARAMS_SIZE]; int offset; struct rename_data *rd = (struct rename_data *)data; if (strncmp(name, rd->old, strlen(rd->old)) != 0) return 0; for (offset = strlen(rd->old); name[offset] && !(isdigit(name[offset])); offset++); /* do nothing */ snprintf(buff, PARAMS_SIZE, "%s%s%s", rd->new, rd->delim, name + offset); dm_rename(name, buff); condlog(4, "partition map %s renamed", name); return 0; } int dm_rename_partmaps (char * old, char * new) { struct rename_data rd; rd.old = old; rd.new = new; if (conf->partition_delim) rd.delim = conf->partition_delim; if (isdigit(new[strlen(new)-1])) rd.delim = "p"; else rd.delim = ""; return do_foreach_partmaps(old, rename_partmap, &rd); } int dm_rename (char * old, char * new) { int r = 0; struct dm_task *dmt; uint32_t cookie; if (dm_rename_partmaps(old, new)) return r; if (!(dmt = dm_task_create(DM_DEVICE_RENAME))) return r; if (!dm_task_set_name(dmt, old)) goto out; if (!dm_task_set_newname(dmt, new)) goto out; dm_task_no_open_count(dmt); if (!dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) goto out; r = dm_task_run(dmt); if (!r) dm_udev_complete(cookie); else dm_udev_wait(cookie); out: dm_task_destroy(dmt); return r; } void dm_reassign_deps(char *table, char *dep, char *newdep) { char *p, *n; char newtable[PARAMS_SIZE]; strcpy(newtable, table); p = strstr(newtable, dep); n = table + (p - newtable); strcpy(n, newdep); n += strlen(newdep); p += strlen(dep); strcat(n, p); } int dm_reassign_table(const char *name, char *old, char *new) { int r, modified = 0; uint64_t start, length; struct dm_task *dmt, *reload_dmt; char *target, *params = NULL; char buff[PARAMS_SIZE]; void *next = NULL; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(reload_dmt = dm_task_create(DM_DEVICE_RELOAD))) goto out; if (!dm_task_set_name(reload_dmt, name)) goto out_reload; do { next = dm_get_next_target(dmt, next, &start, &length, &target, ¶ms); memset(buff, 0, PARAMS_SIZE); strcpy(buff, params); if (strcmp(target, TGT_MPATH) && strstr(params, old)) { condlog(3, "%s: replace target %s %s", name, target, buff); dm_reassign_deps(buff, old, new); condlog(3, "%s: with target %s %s", name, target, buff); modified++; } dm_task_add_target(reload_dmt, start, length, target, buff); } while (next); if (modified) { dm_task_no_open_count(reload_dmt); if (!dm_task_run(reload_dmt)) { condlog(3, "%s: failed to reassign targets", name); goto out_reload; } dm_simplecmd_noflush(DM_DEVICE_RESUME, name, 1, MPATH_UDEV_RELOAD_FLAG); } r = 1; out_reload: dm_task_destroy(reload_dmt); out: dm_task_destroy(dmt); return r; } /* * Reassign existing device-mapper table(s) to not use * the block devices but point to the multipathed * device instead */ int dm_reassign(const char *mapname) { struct dm_deps *deps; struct dm_task *dmt; struct dm_info info; char dev_t[32], dm_dep[32]; int r = 0, i; if (dm_dev_t(mapname, &dev_t[0], 32)) { condlog(3, "%s: failed to get device number", mapname); return 1; } if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) { condlog(3, "%s: couldn't make dm task", mapname); return 0; } if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!(deps = dm_task_get_deps(dmt))) goto out; if (!info.exists) goto out; for (i = 0; i < deps->count; i++) { sprintf(dm_dep, "%d:%d", major(deps->device[i]), minor(deps->device[i])); sysfs_check_holders(dm_dep, dev_t); } dm_task_destroy (dmt); r = 1; out: return r; } int dm_setgeometry(struct multipath *mpp) { struct dm_task *dmt; struct path *pp; char heads[4], sectors[4]; char cylinders[10], start[32]; int r = 0; if (!mpp) return 1; pp = first_path(mpp); if (!pp) { condlog(3, "%s: no path for geometry", mpp->alias); return 1; } if (pp->geom.cylinders == 0 || pp->geom.heads == 0 || pp->geom.sectors == 0) { condlog(3, "%s: invalid geometry on %s", mpp->alias, pp->dev); return 1; } if (!(dmt = dm_task_create(DM_DEVICE_SET_GEOMETRY))) return 0; if (!dm_task_set_name(dmt, mpp->alias)) goto out; dm_task_no_open_count(dmt); /* What a sick interface ... */ snprintf(heads, 4, "%u", pp->geom.heads); snprintf(sectors, 4, "%u", pp->geom.sectors); snprintf(cylinders, 10, "%u", pp->geom.cylinders); snprintf(start, 32, "%lu", pp->geom.start); if (!dm_task_set_geometry(dmt, cylinders, heads, sectors, start)) { condlog(3, "%s: Failed to set geometry", mpp->alias); goto out; } r = dm_task_run(dmt); out: dm_task_destroy(dmt); return r; } multipath-tools-0.5.0+git1.656f8865/libmultipath/devmapper.h000066400000000000000000000042061260771327300234050ustar00rootroot00000000000000#ifndef _DEVMAPPER_H #define _DEVMAPPER_H #include "structs.h" #define TGT_MPATH "multipath" #define TGT_PART "linear" #ifdef DM_SUBSYSTEM_UDEV_FLAG0 #define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 #else #define MPATH_UDEV_RELOAD_FLAG 0 #endif void dm_init(void); int dm_prereq (void); int dm_drv_version (unsigned int * version, char * str); int dm_simplecmd_flush (int, const char *, int, uint16_t); int dm_simplecmd_noflush (int, const char *, int, uint16_t); int dm_addmap_create (struct multipath *mpp, char *params); int dm_addmap_reload (struct multipath *mpp, char *params); int dm_map_present (const char *); int dm_get_map(const char *, unsigned long long *, char *); int dm_get_status(char *, char *); int dm_type(const char *, char *); int dm_is_mpath(const char *); int _dm_flush_map (const char *, int, int); int dm_flush_map_nopaths(const char * mapname, int deferred_remove); #define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0) #define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0, 0) int dm_cancel_deferred_remove(struct multipath *mpp); int dm_suspend_and_flush_map(const char * mapname); int dm_flush_maps (void); int dm_fail_path(char * mapname, char * path); int dm_reinstate_path(char * mapname, char * path); int dm_queue_if_no_path(char *mapname, int enable); int dm_switchgroup(char * mapname, int index); int dm_enablegroup(char * mapname, int index); int dm_disablegroup(char * mapname, int index); int dm_get_maps (vector mp); int dm_geteventnr (char *name); int dm_get_major (char *name); int dm_get_minor (char *name); char * dm_mapname(int major, int minor); int dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove); int dm_get_uuid(char *name, char *uuid); int dm_get_info (char * mapname, struct dm_info ** dmi); int dm_rename (char * old, char * new); int dm_reassign(const char * mapname); int dm_reassign_table(const char *name, char *old, char *new); int dm_setgeometry(struct multipath *mpp); #define VERSION_GE(v, minv) ( \ (v[0] > minv[0]) || \ ((v[0] == minv[0]) && (v[1] > minv[1])) || \ ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \ ) #endif /* _DEVMAPPER_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/dict.c000066400000000000000000001263311260771327300223440ustar00rootroot00000000000000/* * Based on Alexandre Cassen template for keepalived * Copyright (c) 2004, 2005, 2006 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include #include #include #include "checkers.h" #include "vector.h" #include "hwtable.h" #include "structs.h" #include "parser.h" #include "config.h" #include "debug.h" #include "memory.h" #include "pgpolicies.h" #include "blacklist.h" #include "defaults.h" #include "prio.h" #include "errno.h" #include static int set_int(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = VECTOR_SLOT(strvec, 1); *int_ptr = atoi(buff); return 0; } static int set_str(vector strvec, void *ptr) { char **str_ptr = (char **)ptr; if (*str_ptr) FREE(*str_ptr); *str_ptr = set_value(strvec); if (!*str_ptr) return 1; return 0; } static int set_yes_no(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YN_YES; else *int_ptr = YN_NO; FREE(buff); return 0; } static int set_yes_no_undef(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) *int_ptr = YNU_NO; else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YNU_YES; else *int_ptr = YNU_UNDEF; FREE(buff); return 0; } static int print_int (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; return snprintf(buff, len, "%i", *int_ptr); } static int print_nonzero (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; return snprintf(buff, len, "%i", *int_ptr); } static int print_str (char *buff, int len, void *ptr) { char **str_ptr = (char **)ptr; if (!*str_ptr) return 0; return snprintf(buff, len, "\"%s\"", *str_ptr); } static int print_yes_no (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; return snprintf(buff, len, "\"%s\"", (*int_ptr == YN_NO)? "no" : "yes"); } static int print_yes_no_undef (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; return snprintf(buff, len, "\"%s\"", (*int_ptr == YNU_NO)? "no" : "yes"); } #define declare_def_handler(option, function) \ static int \ def_ ## option ## _handler (vector strvec) \ { \ return function (strvec, &conf->option); \ } #define declare_def_snprint(option, function) \ static int \ snprint_def_ ## option (char * buff, int len, void * data) \ { \ return function (buff, len, &conf->option); \ } #define declare_def_snprint_defint(option, function, value) \ static int \ snprint_def_ ## option (char * buff, int len, void * data) \ { \ int i = value; \ if (!conf->option) \ return function (buff, len, &i); \ return function (buff, len, &conf->option); \ } #define declare_def_snprint_defstr(option, function, value) \ static int \ snprint_def_ ## option (char * buff, int len, void * data) \ { \ char *s = value; \ if (!conf->option) \ return function (buff, len, &s); \ return function (buff, len, &conf->option); \ } #define declare_hw_handler(option, function) \ static int \ hw_ ## option ## _handler (vector strvec) \ { \ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ if (!hwe) \ return 1; \ return function (strvec, &hwe->option); \ } #define declare_hw_snprint(option, function) \ static int \ snprint_hw_ ## option (char * buff, int len, void * data) \ { \ struct hwentry * hwe = (struct hwentry *)data; \ return function (buff, len, &hwe->option); \ } #define declare_ovr_handler(option, function) \ static int \ ovr_ ## option ## _handler (vector strvec) \ { \ if (!conf->overrides) \ return 1; \ return function (strvec, &conf->overrides->option); \ } #define declare_ovr_snprint(option, function) \ static int \ snprint_ovr_ ## option (char * buff, int len, void * data) \ { \ return function (buff, len, &conf->overrides->option); \ } #define declare_mp_handler(option, function) \ static int \ mp_ ## option ## _handler (vector strvec) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ return function (strvec, &mpe->option); \ } #define declare_mp_snprint(option, function) \ static int \ snprint_mp_ ## option (char * buff, int len, void * data) \ { \ struct mpentry * mpe = (struct mpentry *)data; \ return function (buff, len, &mpe->option); \ } declare_def_handler(checkint, set_int) declare_def_snprint(checkint, print_int) declare_def_handler(max_checkint, set_int) declare_def_snprint(max_checkint, print_int) declare_def_handler(verbosity, set_int) declare_def_snprint(verbosity, print_int) declare_def_handler(reassign_maps, set_yes_no) declare_def_snprint(reassign_maps, print_yes_no) declare_def_handler(multipath_dir, set_str) 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) declare_def_handler(selector, set_str) declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) declare_hw_handler(selector, set_str) declare_hw_snprint(selector, print_str) declare_ovr_handler(selector, set_str) declare_ovr_snprint(selector, print_str) declare_mp_handler(selector, set_str) declare_mp_snprint(selector, print_str) declare_def_handler(uid_attribute, set_str) declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE) declare_ovr_handler(uid_attribute, set_str) declare_ovr_snprint(uid_attribute, print_str) declare_hw_handler(uid_attribute, set_str) declare_hw_snprint(uid_attribute, print_str) declare_def_handler(getuid, set_str) declare_def_snprint(getuid, print_str) declare_ovr_handler(getuid, set_str) declare_ovr_snprint(getuid, print_str) declare_hw_handler(getuid, set_str) declare_hw_snprint(getuid, print_str) declare_def_handler(prio_name, set_str) declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO) declare_ovr_handler(prio_name, set_str) declare_ovr_snprint(prio_name, print_str) declare_hw_handler(prio_name, set_str) declare_hw_snprint(prio_name, print_str) declare_mp_handler(prio_name, set_str) declare_mp_snprint(prio_name, print_str) declare_def_handler(alias_prefix, set_str) declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX) declare_ovr_handler(alias_prefix, set_str) declare_ovr_snprint(alias_prefix, print_str) declare_hw_handler(alias_prefix, set_str) declare_hw_snprint(alias_prefix, print_str) declare_def_handler(prio_args, set_str) declare_def_snprint_defstr(prio_args, print_str, DEFAULT_PRIO_ARGS) declare_ovr_handler(prio_args, set_str) declare_ovr_snprint(prio_args, print_str) declare_hw_handler(prio_args, set_str) declare_hw_snprint(prio_args, print_str) declare_mp_handler(prio_args, set_str) declare_mp_snprint(prio_args, print_str) declare_def_handler(features, set_str) declare_def_snprint_defstr(features, print_str, DEFAULT_FEATURES) declare_ovr_handler(features, set_str) declare_ovr_snprint(features, print_str) declare_hw_handler(features, set_str) declare_hw_snprint(features, print_str) declare_mp_handler(features, set_str) declare_mp_snprint(features, print_str) declare_def_handler(checker_name, set_str) declare_def_snprint_defstr(checker_name, print_str, DEFAULT_CHECKER) declare_ovr_handler(checker_name, set_str) declare_ovr_snprint(checker_name, print_str) declare_hw_handler(checker_name, set_str) declare_hw_snprint(checker_name, print_str) declare_def_handler(minio, set_int) declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO) declare_ovr_handler(minio, set_int) declare_ovr_snprint(minio, print_nonzero) declare_hw_handler(minio, set_int) declare_hw_snprint(minio, print_nonzero) declare_mp_handler(minio, set_int) declare_mp_snprint(minio, print_nonzero) declare_def_handler(minio_rq, set_int) declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ) declare_ovr_handler(minio_rq, set_int) declare_ovr_snprint(minio_rq, print_nonzero) declare_hw_handler(minio_rq, set_int) declare_hw_snprint(minio_rq, print_nonzero) declare_mp_handler(minio_rq, set_int) declare_mp_snprint(minio_rq, print_nonzero) declare_def_handler(queue_without_daemon, set_yes_no) static int snprint_def_queue_without_daemon (char * buff, int len, void * data) { switch (conf->queue_without_daemon) { case QUE_NO_DAEMON_OFF: return snprintf(buff, len, "\"no\""); case QUE_NO_DAEMON_ON: return snprintf(buff, len, "\"yes\""); case QUE_NO_DAEMON_FORCE: return snprintf(buff, len, "\"forced\""); } return 0; } declare_def_handler(checker_timeout, set_int) declare_def_snprint(checker_timeout, print_nonzero) declare_def_handler(flush_on_last_del, set_yes_no_undef) declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, YNU_NO) declare_ovr_handler(flush_on_last_del, set_yes_no_undef) declare_ovr_snprint(flush_on_last_del, print_yes_no_undef) declare_hw_handler(flush_on_last_del, set_yes_no_undef) declare_hw_snprint(flush_on_last_del, print_yes_no_undef) declare_mp_handler(flush_on_last_del, set_yes_no_undef) declare_mp_snprint(flush_on_last_del, print_yes_no_undef) declare_def_handler(user_friendly_names, set_yes_no_undef) declare_def_snprint_defint(user_friendly_names, print_yes_no_undef, YNU_NO) declare_ovr_handler(user_friendly_names, set_yes_no_undef) declare_ovr_snprint(user_friendly_names, print_yes_no_undef) declare_hw_handler(user_friendly_names, set_yes_no_undef) declare_hw_snprint(user_friendly_names, print_yes_no_undef) declare_mp_handler(user_friendly_names, set_yes_no_undef) declare_mp_snprint(user_friendly_names, print_yes_no_undef) declare_def_handler(bindings_file, set_str) declare_def_snprint(bindings_file, print_str) declare_def_handler(wwids_file, set_str) declare_def_snprint(wwids_file, print_str) declare_def_handler(retain_hwhandler, set_yes_no_undef) declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO) declare_ovr_handler(retain_hwhandler, set_yes_no_undef) declare_ovr_snprint(retain_hwhandler, print_yes_no_undef) declare_hw_handler(retain_hwhandler, set_yes_no_undef) declare_hw_snprint(retain_hwhandler, print_yes_no_undef) declare_def_handler(detect_prio, set_yes_no_undef) declare_def_snprint_defint(detect_prio, print_yes_no_undef, YNU_NO) declare_ovr_handler(detect_prio, set_yes_no_undef) declare_ovr_snprint(detect_prio, print_yes_no_undef) declare_hw_handler(detect_prio, set_yes_no_undef) declare_hw_snprint(detect_prio, print_yes_no_undef) declare_def_handler(force_sync, set_yes_no) declare_def_snprint(force_sync, print_yes_no) declare_def_handler(deferred_remove, set_yes_no_undef) declare_def_snprint_defint(deferred_remove, print_yes_no_undef, YNU_NO) declare_ovr_handler(deferred_remove, set_yes_no_undef) declare_ovr_snprint(deferred_remove, print_yes_no_undef) declare_hw_handler(deferred_remove, set_yes_no_undef) declare_hw_snprint(deferred_remove, print_yes_no_undef) declare_mp_handler(deferred_remove, set_yes_no_undef) declare_mp_snprint(deferred_remove, print_yes_no_undef) static int def_config_dir_handler(vector strvec) { /* this is only valid in the main config file */ if (conf->processed_main_config) return 0; return set_str(strvec, &conf->config_dir); } declare_def_snprint(config_dir, print_str) #define declare_def_attr_handler(option, function) \ static int \ def_ ## option ## _handler (vector strvec) \ { \ return function (strvec, &conf->option, &conf->attribute_flags);\ } #define declare_def_attr_snprint(option, function) \ static int \ snprint_def_ ## option (char * buff, int len, void * data) \ { \ return function (buff, len, &conf->option, \ &conf->attribute_flags); \ } #define declare_mp_attr_handler(option, function) \ static int \ mp_ ## option ## _handler (vector strvec) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ return function (strvec, &mpe->option, &mpe->attribute_flags); \ } #define declare_mp_attr_snprint(option, function) \ static int \ snprint_mp_ ## option (char * buff, int len, void * data) \ { \ struct mpentry * mpe = (struct mpentry *)data; \ return function (buff, len, &mpe->option, \ &mpe->attribute_flags); \ } static int set_mode(vector strvec, void *ptr, int *flags) { mode_t mode; mode_t *mode_ptr = (mode_t *)ptr; char *buff; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { *flags |= (1 << ATTR_MODE); *mode_ptr = mode; } FREE(buff); return 0; } static int set_uid(vector strvec, void *ptr, int *flags) { uid_t uid; uid_t *uid_ptr = (uid_t *)ptr; char *buff; char passwd_buf[1024]; struct passwd info, *found; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { *flags |= (1 << ATTR_UID); *uid_ptr = info.pw_uid; } else if (sscanf(buff, "%u", &uid) == 1){ *flags |= (1 << ATTR_UID); *uid_ptr = uid; } FREE(buff); return 0; } static int set_gid(vector strvec, void *ptr, int *flags) { gid_t gid; gid_t *gid_ptr = (gid_t *)ptr; char *buff; char passwd_buf[1024]; struct passwd info, *found; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { *flags |= (1 << ATTR_GID); *gid_ptr = info.pw_gid; } else if (sscanf(buff, "%u", &gid) == 1){ *flags |= (1 << ATTR_GID); *gid_ptr = gid; } FREE(buff); return 0; } static int print_mode(char * buff, int len, void *ptr, int *flags) { mode_t *mode_ptr = (mode_t *)ptr; if ((*flags & (1 << ATTR_MODE)) == 0) return 0; return snprintf(buff, len, "0%o", *mode_ptr); } static int print_uid(char * buff, int len, void *ptr, int *flags) { uid_t *uid_ptr = (uid_t *)ptr; if ((*flags & (1 << ATTR_UID)) == 0) return 0; return snprintf(buff, len, "0%o", *uid_ptr); } static int print_gid(char * buff, int len, void *ptr, int *flags) { gid_t *gid_ptr = (gid_t *)ptr; if ((*flags & (1 << ATTR_GID)) == 0) return 0; return snprintf(buff, len, "0%o", *gid_ptr); } declare_def_attr_handler(mode, set_mode) declare_def_attr_snprint(mode, print_mode) declare_mp_attr_handler(mode, set_mode) declare_mp_attr_snprint(mode, print_mode) declare_def_attr_handler(uid, set_uid) declare_def_attr_snprint(uid, print_uid) declare_mp_attr_handler(uid, set_uid) declare_mp_attr_snprint(uid, print_uid) declare_def_attr_handler(gid, set_gid) declare_def_attr_snprint(gid, print_gid) declare_mp_attr_handler(gid, set_gid) declare_mp_attr_snprint(gid, print_gid) static int set_fast_io_fail(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "off") == 0) *int_ptr = MP_FAST_IO_FAIL_OFF; else if (sscanf(buff, "%d", int_ptr) != 1 || *int_ptr < MP_FAST_IO_FAIL_ZERO) *int_ptr = MP_FAST_IO_FAIL_UNSET; else if (*int_ptr == 0) *int_ptr = MP_FAST_IO_FAIL_ZERO; FREE(buff); return 0; } int print_fast_io_fail(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (*int_ptr == MP_FAST_IO_FAIL_UNSET) return 0; if (*int_ptr == MP_FAST_IO_FAIL_OFF) return snprintf(buff, len, "\"off\""); if (*int_ptr == MP_FAST_IO_FAIL_ZERO) return snprintf(buff, len, "0"); return snprintf(buff, len, "%d", *int_ptr); } declare_def_handler(fast_io_fail, set_fast_io_fail) declare_def_snprint(fast_io_fail, print_fast_io_fail) declare_ovr_handler(fast_io_fail, set_fast_io_fail) declare_ovr_snprint(fast_io_fail, print_fast_io_fail) declare_hw_handler(fast_io_fail, set_fast_io_fail) declare_hw_snprint(fast_io_fail, print_fast_io_fail) static int set_dev_loss(vector strvec, void *ptr) { char * buff; unsigned int *uint_ptr = (unsigned int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "infinity")) *uint_ptr = MAX_DEV_LOSS_TMO; else if (sscanf(buff, "%u", uint_ptr) != 1) *uint_ptr = 0; FREE(buff); return 0; } int print_dev_loss(char * buff, int len, void *ptr) { unsigned int *uint_ptr = (unsigned int *)ptr; if (!*uint_ptr) return 0; if (*uint_ptr >= MAX_DEV_LOSS_TMO) return snprintf(buff, len, "\"infinity\""); return snprintf(buff, len, "%u", *uint_ptr); } declare_def_handler(dev_loss, set_dev_loss) declare_def_snprint(dev_loss, print_dev_loss) declare_ovr_handler(dev_loss, set_dev_loss) declare_ovr_snprint(dev_loss, print_dev_loss) declare_hw_handler(dev_loss, set_dev_loss) declare_hw_snprint(dev_loss, print_dev_loss) static int set_pgpolicy(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; *int_ptr = get_pgpolicy_id(buff); FREE(buff); return 0; } int print_pgpolicy(char * buff, int len, void *ptr) { char str[POLICY_NAME_SIZE]; int pgpolicy = *(int *)ptr; if (!pgpolicy) return 0; get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy); return snprintf(buff, len, "\"%s\"", str); } declare_def_handler(pgpolicy, set_pgpolicy) declare_def_snprint_defint(pgpolicy, print_pgpolicy, DEFAULT_PGPOLICY) declare_ovr_handler(pgpolicy, set_pgpolicy) declare_ovr_snprint(pgpolicy, print_pgpolicy) declare_hw_handler(pgpolicy, set_pgpolicy) declare_hw_snprint(pgpolicy, print_pgpolicy) declare_mp_handler(pgpolicy, set_pgpolicy) declare_mp_snprint(pgpolicy, print_pgpolicy) int get_sys_max_fds(int *max_fds) { FILE *file; int nr_open; int ret = 1; file = fopen("/proc/sys/fs/nr_open", "r"); if (!file) { fprintf(stderr, "Cannot open /proc/sys/fs/nr_open : %s\n", strerror(errno)); return 1; } if (fscanf(file, "%d", &nr_open) != 1) { fprintf(stderr, "Cannot read max open fds from /proc/sys/fs/nr_open"); if (ferror(file)) fprintf(stderr, " : %s\n", strerror(errno)); else fprintf(stderr, "\n"); } else { *max_fds = nr_open; ret = 0; } fclose(file); return ret; } static int max_fds_handler(vector strvec) { char * buff; int r = 0, max_fds; buff = set_value(strvec); if (!buff) return 1; r = get_sys_max_fds(&max_fds); if (r) { /* Assume safe limit */ max_fds = 4096; } if (strlen(buff) == 3 && !strcmp(buff, "max")) conf->max_fds = max_fds; else conf->max_fds = atoi(buff); if (conf->max_fds > max_fds) conf->max_fds = max_fds; FREE(buff); return r; } static int snprint_max_fds (char * buff, int len, void * data) { int r = 0, max_fds; if (!conf->max_fds) return 0; r = get_sys_max_fds(&max_fds); if (!r && conf->max_fds >= max_fds) return snprintf(buff, len, "\"max\""); else return snprintf(buff, len, "%d", conf->max_fds); } static int set_rr_weight(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "priorities")) *int_ptr = RR_WEIGHT_PRIO; if (!strcmp(buff, "uniform")) *int_ptr = RR_WEIGHT_NONE; FREE(buff); return 0; } int print_rr_weight (char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; if (*int_ptr == RR_WEIGHT_PRIO) return snprintf(buff, len, "\"priorities\""); if (*int_ptr == RR_WEIGHT_NONE) return snprintf(buff, len, "\"uniform\""); return 0; } declare_def_handler(rr_weight, set_rr_weight) declare_def_snprint_defint(rr_weight, print_rr_weight, RR_WEIGHT_NONE) declare_ovr_handler(rr_weight, set_rr_weight) declare_ovr_snprint(rr_weight, print_rr_weight) declare_hw_handler(rr_weight, set_rr_weight) declare_hw_snprint(rr_weight, print_rr_weight) declare_mp_handler(rr_weight, set_rr_weight) declare_mp_snprint(rr_weight, print_rr_weight) static int set_pgfailback(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (strlen(buff) == 6 && !strcmp(buff, "manual")) *int_ptr = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) *int_ptr = -FAILBACK_IMMEDIATE; else if (strlen(buff) == 10 && !strcmp(buff, "followover")) *int_ptr = -FAILBACK_FOLLOWOVER; else *int_ptr = atoi(buff); FREE(buff); return 0; } int print_pgfailback (char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case FAILBACK_UNDEF: return 0; case -FAILBACK_MANUAL: return snprintf(buff, len, "\"manual\""); case -FAILBACK_IMMEDIATE: return snprintf(buff, len, "\"immediate\""); case -FAILBACK_FOLLOWOVER: return snprintf(buff, len, "\"followover\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(pgfailback, set_pgfailback) declare_def_snprint_defint(pgfailback, print_pgfailback, DEFAULT_FAILBACK) declare_ovr_handler(pgfailback, set_pgfailback) declare_ovr_snprint(pgfailback, print_pgfailback) declare_hw_handler(pgfailback, set_pgfailback) declare_hw_snprint(pgfailback, print_pgfailback) declare_mp_handler(pgfailback, set_pgfailback) declare_mp_snprint(pgfailback, print_pgfailback) static int set_no_path_retry(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "fail") || !strcmp(buff, "0")) *int_ptr = NO_PATH_RETRY_FAIL; else if (!strcmp(buff, "queue")) *int_ptr = NO_PATH_RETRY_QUEUE; else if ((*int_ptr = atoi(buff)) < 1) *int_ptr = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } int print_no_path_retry(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case NO_PATH_RETRY_UNDEF: return 0; case NO_PATH_RETRY_FAIL: return snprintf(buff, len, "\"fail\""); case NO_PATH_RETRY_QUEUE: return snprintf(buff, len, "\"queue\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(no_path_retry, set_no_path_retry) declare_def_snprint(no_path_retry, print_no_path_retry) declare_ovr_handler(no_path_retry, set_no_path_retry) declare_ovr_snprint(no_path_retry, print_no_path_retry) declare_hw_handler(no_path_retry, set_no_path_retry) declare_hw_snprint(no_path_retry, print_no_path_retry) declare_mp_handler(no_path_retry, set_no_path_retry) declare_mp_snprint(no_path_retry, print_no_path_retry) static int def_log_checker_err_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 4 && !strcmp(buff, "once")) conf->log_checker_err = LOG_CHKR_ERR_ONCE; else if (strlen(buff) == 6 && !strcmp(buff, "always")) conf->log_checker_err = LOG_CHKR_ERR_ALWAYS; free(buff); return 0; } static int snprint_def_log_checker_err (char * buff, int len, void * data) { if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) return snprintf(buff, len, "once"); return snprintf(buff, len, "always"); } static int set_reservation_key(vector strvec, void *ptr) { unsigned char **uchar_ptr = (unsigned char **)ptr; char *buff; char *tbuff; int j, k; int len; uint64_t prkey; buff = set_value(strvec); if (!buff) return 1; tbuff = buff; if (!memcmp("0x",buff, 2)) buff = buff + 2; len = strlen(buff); k = strspn(buff, "0123456789aAbBcCdDeEfF"); if (len != k) { FREE(tbuff); return 1; } if (1 != sscanf (buff, "%" SCNx64 "", &prkey)) { FREE(tbuff); return 1; } if (!*uchar_ptr) *uchar_ptr = (unsigned char *) malloc(8); memset(*uchar_ptr, 0, 8); for (j = 7; j >= 0; --j) { (*uchar_ptr)[j] = (prkey & 0xff); prkey >>= 8; } FREE(tbuff); return 0; } int print_reservation_key(char * buff, int len, void * ptr) { unsigned char **uchar_ptr = (unsigned char **)ptr; int i; unsigned char *keyp; uint64_t prkey = 0; if (!*uchar_ptr) return 0; keyp = (unsigned char *)(*uchar_ptr); for (i = 0; i < 8; i++) { if (i > 0) prkey <<= 8; prkey |= *keyp; keyp++; } return snprintf(buff, len, "0x%" PRIx64, prkey); } declare_def_handler(reservation_key, set_reservation_key) declare_def_snprint(reservation_key, print_reservation_key) declare_mp_handler(reservation_key, set_reservation_key) declare_mp_snprint(reservation_key, print_reservation_key) static int set_delay_checks(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "no") || !strcmp(buff, "0")) *int_ptr = DELAY_CHECKS_OFF; else if ((*int_ptr = atoi(buff)) < 1) *int_ptr = DELAY_CHECKS_UNDEF; FREE(buff); return 0; } int print_delay_checks(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case DELAY_CHECKS_UNDEF: return 0; case DELAY_CHECKS_OFF: return snprintf(buff, len, "\"off\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(delay_watch_checks, set_delay_checks) declare_def_snprint(delay_watch_checks, print_delay_checks) declare_ovr_handler(delay_watch_checks, set_delay_checks) declare_ovr_snprint(delay_watch_checks, print_delay_checks) declare_hw_handler(delay_watch_checks, set_delay_checks) declare_hw_snprint(delay_watch_checks, print_delay_checks) declare_mp_handler(delay_watch_checks, set_delay_checks) declare_mp_snprint(delay_watch_checks, print_delay_checks) declare_def_handler(delay_wait_checks, set_delay_checks) declare_def_snprint(delay_wait_checks, print_delay_checks) declare_ovr_handler(delay_wait_checks, set_delay_checks) declare_ovr_snprint(delay_wait_checks, print_delay_checks) declare_hw_handler(delay_wait_checks, set_delay_checks) declare_hw_snprint(delay_wait_checks, print_delay_checks) declare_mp_handler(delay_wait_checks, set_delay_checks) declare_mp_snprint(delay_wait_checks, print_delay_checks) static int def_uxsock_timeout_handler(vector strvec) { unsigned int uxsock_timeout; char *buff; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%u", &uxsock_timeout) == 1 && uxsock_timeout > DEFAULT_UXSOCK_TIMEOUT) conf->uxsock_timeout = uxsock_timeout; else conf->uxsock_timeout = DEFAULT_UXSOCK_TIMEOUT; free(buff); return 0; } /* * blacklist block handlers */ static int blacklist_handler(vector strvec) { if (!conf->blist_devnode) conf->blist_devnode = vector_alloc(); if (!conf->blist_wwid) conf->blist_wwid = vector_alloc(); if (!conf->blist_device) conf->blist_device = vector_alloc(); if (!conf->blist_property) conf->blist_property = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device || !conf->blist_property) return 1; return 0; } static int blacklist_exceptions_handler(vector strvec) { if (!conf->elist_devnode) conf->elist_devnode = vector_alloc(); if (!conf->elist_wwid) conf->elist_wwid = vector_alloc(); if (!conf->elist_device) conf->elist_device = vector_alloc(); if (!conf->elist_property) conf->elist_property = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device || !conf->elist_property) return 1; return 0; } #define declare_ble_handler(option) \ static int \ ble_ ## option ## _handler (vector strvec) \ { \ char * buff; \ \ if (!conf->option) \ return 1; \ \ buff = set_value(strvec); \ if (!buff) \ return 1; \ \ return store_ble(conf->option, buff, ORIGIN_CONFIG); \ } #define declare_ble_device_handler(name, option, vend, prod) \ static int \ ble_ ## option ## _ ## name ## _handler (vector strvec) \ { \ char * buff; \ \ if (!conf->option) \ return 1; \ \ buff = set_value(strvec); \ if (!buff) \ return 1; \ \ return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \ } declare_ble_handler(blist_devnode) declare_ble_handler(elist_devnode) declare_ble_handler(blist_wwid) declare_ble_handler(elist_wwid) declare_ble_handler(blist_property) declare_ble_handler(elist_property) static int snprint_def_uxsock_timeout(char * buff, int len, void * data) { if (conf->uxsock_timeout == DEFAULT_UXSOCK_TIMEOUT) return 0; return snprintf(buff, len, "%u", conf->uxsock_timeout); } static int snprint_ble_simple (char * buff, int len, void * data) { struct blentry * ble = (struct blentry *)data; return snprintf(buff, len, "\"%s\"", ble->str); } static int ble_device_handler(vector strvec) { return alloc_ble_device(conf->blist_device); } static int ble_except_device_handler(vector strvec) { return alloc_ble_device(conf->elist_device); } declare_ble_device_handler(vendor, blist_device, buff, NULL) declare_ble_device_handler(vendor, elist_device, buff, NULL) declare_ble_device_handler(product, blist_device, NULL, buff) declare_ble_device_handler(product, elist_device, NULL, buff) static int snprint_bled_vendor (char * buff, int len, void * data) { struct blentry_device * bled = (struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->vendor); } static int snprint_bled_product (char * buff, int len, void * data) { struct blentry_device * bled = (struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->product); } /* * devices block handlers */ static int devices_handler(vector strvec) { if (!conf->hwtable) conf->hwtable = vector_alloc(); if (!conf->hwtable) return 1; return 0; } static int device_handler(vector strvec) { struct hwentry * hwe; hwe = alloc_hwe(); if (!hwe) return 1; if (!vector_alloc_slot(conf->hwtable)) { free_hwe(hwe); return 1; } vector_set_slot(conf->hwtable, hwe); return 0; } declare_hw_handler(vendor, set_str) declare_hw_snprint(vendor, print_str) declare_hw_handler(product, set_str) declare_hw_snprint(product, print_str) declare_hw_handler(revision, set_str) declare_hw_snprint(revision, print_str) declare_hw_handler(bl_product, set_str) declare_hw_snprint(bl_product, print_str) declare_hw_handler(hwhandler, set_str) declare_hw_snprint(hwhandler, print_str) /* * overrides handlers */ static int overrides_handler(vector strvec) { if (!conf->overrides) conf->overrides = alloc_hwe(); if (!conf->overrides) return 1; return 0; } /* * multipaths block handlers */ static int multipaths_handler(vector strvec) { if (!conf->mptable) conf->mptable = vector_alloc(); if (!conf->mptable) return 1; return 0; } static int multipath_handler(vector strvec) { struct mpentry * mpe; mpe = alloc_mpe(); if (!mpe) return 1; if (!vector_alloc_slot(conf->mptable)) { free_mpe(mpe); return 1; } vector_set_slot(conf->mptable, mpe); return 0; } declare_mp_handler(wwid, set_str) declare_mp_snprint(wwid, print_str) declare_mp_handler(alias, set_str) declare_mp_snprint(alias, print_str) /* * deprecated handlers */ static int deprecated_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; FREE(buff); return 0; } static int snprint_deprecated (char * buff, int len, void * data) { return 0; } #define __deprecated void init_keywords(void) { install_keyword_root("defaults", NULL); install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity); install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint); install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint); install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps); install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir); install_keyword("path_selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy); install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute); install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid); install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name); install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args); install_keyword("features", &def_features_handler, &snprint_def_features); install_keyword("path_checker", &def_checker_name_handler, &snprint_def_checker_name); install_keyword("checker", &def_checker_name_handler, NULL); install_keyword("alias_prefix", &def_alias_prefix_handler, &snprint_def_alias_prefix); install_keyword("failback", &def_pgfailback_handler, &snprint_def_pgfailback); install_keyword("rr_min_io", &def_minio_handler, &snprint_def_minio); install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_minio_rq); install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); install_keyword("rr_weight", &def_rr_weight_handler, &snprint_def_rr_weight); install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon); install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del); install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names); install_keyword("mode", &def_mode_handler, &snprint_def_mode); install_keyword("uid", &def_uid_handler, &snprint_def_uid); install_keyword("gid", &def_gid_handler, &snprint_def_gid); install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail); install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss); install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file); install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file); install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err); install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key); install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler); install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio); install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync); install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove); install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim); install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir); install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks); install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks); install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths); install_keyword("uxsock_timeout", &def_uxsock_timeout_handler, &snprint_def_uxsock_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); __deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); __deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL); install_keyword_root("blacklist", &blacklist_handler); install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_blist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_blist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_blist_device_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_blist_device_product_handler, &snprint_bled_product); install_sublevel_end(); install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); install_keyword_multi("devnode", &ble_elist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_elist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_elist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_except_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_elist_device_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product); install_sublevel_end(); #if 0 __deprecated install_keyword_root("devnode_blacklist", &blacklist_handler); __deprecated install_keyword("devnode", &ble_devnode_handler, &snprint_ble_simple); __deprecated install_keyword("wwid", &ble_wwid_handler, &snprint_ble_simple); __deprecated install_keyword("device", &ble_device_handler, NULL); __deprecated install_sublevel(); __deprecated install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); __deprecated install_keyword("product", &ble_product_handler, &snprint_bled_product); __deprecated install_sublevel_end(); #endif install_keyword_root("devices", &devices_handler); install_keyword_multi("device", &device_handler, NULL); install_sublevel(); install_keyword("vendor", &hw_vendor_handler, &snprint_hw_vendor); install_keyword("product", &hw_product_handler, &snprint_hw_product); install_keyword("revision", &hw_revision_handler, &snprint_hw_revision); install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product); install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy); install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute); install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid); install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector); install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name); install_keyword("checker", &hw_checker_name_handler, NULL); install_keyword("alias_prefix", &hw_alias_prefix_handler, &snprint_hw_alias_prefix); install_keyword("features", &hw_features_handler, &snprint_hw_features); install_keyword("hardware_handler", &hw_hwhandler_handler, &snprint_hw_hwhandler); install_keyword("prio", &hw_prio_name_handler, &snprint_hw_prio_name); install_keyword("prio_args", &hw_prio_args_handler, &snprint_hw_prio_args); install_keyword("failback", &hw_pgfailback_handler, &snprint_hw_pgfailback); install_keyword("rr_weight", &hw_rr_weight_handler, &snprint_hw_rr_weight); install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_minio); install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_minio_rq); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del); install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail); install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss); install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names); install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler); install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio); install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove); install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); install_sublevel_end(); install_keyword_root("overrides", &overrides_handler); install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy); install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute); install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid); install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector); install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name); install_keyword("checker", &ovr_checker_name_handler, NULL); install_keyword("alias_prefix", &ovr_alias_prefix_handler, &snprint_ovr_alias_prefix); install_keyword("features", &ovr_features_handler, &snprint_ovr_features); install_keyword("prio", &ovr_prio_name_handler, &snprint_ovr_prio_name); install_keyword("prio_args", &ovr_prio_args_handler, &snprint_ovr_prio_args); install_keyword("failback", &ovr_pgfailback_handler, &snprint_ovr_pgfailback); install_keyword("rr_weight", &ovr_rr_weight_handler, &snprint_ovr_rr_weight); install_keyword("no_path_retry", &ovr_no_path_retry_handler, &snprint_ovr_no_path_retry); install_keyword("rr_min_io", &ovr_minio_handler, &snprint_ovr_minio); install_keyword("rr_min_io_rq", &ovr_minio_rq_handler, &snprint_ovr_minio_rq); install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del); install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail); install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss); install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names); install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler); install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio); install_keyword("deferred_remove", &ovr_deferred_remove_handler, &snprint_ovr_deferred_remove); install_keyword("delay_watch_checks", &ovr_delay_watch_checks_handler, &snprint_ovr_delay_watch_checks); install_keyword("delay_wait_checks", &ovr_delay_wait_checks_handler, &snprint_ovr_delay_wait_checks); install_keyword_root("multipaths", &multipaths_handler); install_keyword_multi("multipath", &multipath_handler, NULL); install_sublevel(); install_keyword("wwid", &mp_wwid_handler, &snprint_mp_wwid); install_keyword("alias", &mp_alias_handler, &snprint_mp_alias); install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_pgpolicy); install_keyword("path_selector", &mp_selector_handler, &snprint_mp_selector); install_keyword("prio", &mp_prio_name_handler, &snprint_mp_prio_name); install_keyword("prio_args", &mp_prio_args_handler, &snprint_mp_prio_args); install_keyword("failback", &mp_pgfailback_handler, &snprint_mp_pgfailback); install_keyword("rr_weight", &mp_rr_weight_handler, &snprint_mp_rr_weight); install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_minio); install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_minio_rq); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &mp_flush_on_last_del_handler, &snprint_mp_flush_on_last_del); install_keyword("features", &mp_features_handler, &snprint_mp_features); install_keyword("mode", &mp_mode_handler, &snprint_mp_mode); install_keyword("uid", &mp_uid_handler, &snprint_mp_uid); install_keyword("gid", &mp_gid_handler, &snprint_mp_gid); install_keyword("reservation_key", &mp_reservation_key_handler, &snprint_mp_reservation_key); install_keyword("user_friendly_names", &mp_user_friendly_names_handler, &snprint_mp_user_friendly_names); install_keyword("deferred_remove", &mp_deferred_remove_handler, &snprint_mp_deferred_remove); install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks); install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks); install_sublevel_end(); } multipath-tools-0.5.0+git1.656f8865/libmultipath/dict.h000066400000000000000000000011351260771327300223430ustar00rootroot00000000000000#ifndef _DICT_H #define _DICT_H #ifndef _VECTOR_H #include "vector.h" #endif void init_keywords(void); 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_reservation_key(char * buff, int len, void * ptr); int print_delay_checks(char * buff, int len, void *ptr); #endif /* _DICT_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/discovery.c000066400000000000000000001147641260771327300234370ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005, 2006 Christophe Varoqui * Copyright (c) 2005 Stefan Bader, IBM * Copyright (c) 2005 Mike Anderson */ #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "util.h" #include "structs.h" #include "config.h" #include "blacklist.h" #include "callout.h" #include "debug.h" #include "propsel.h" #include "sg_include.h" #include "sysfs.h" #include "discovery.h" #include "prio.h" #include "defaults.h" int alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr) { int err = PATHINFO_FAILED; struct path * pp; const char * devname; if (pp_ptr) *pp_ptr = NULL; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; pp = alloc_path(); if (!pp) return PATHINFO_FAILED; if (safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); } else { pp->udev = udev_device_ref(udevice); err = pathinfo(pp, hwtable, flag | DI_BLACKLIST); } if (err) free_path(pp); else if (pp_ptr) *pp_ptr = pp; return err; } int store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr) { int err = PATHINFO_FAILED; struct path * pp; const char * devname; if (pp_ptr) *pp_ptr = NULL; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; pp = alloc_path(); if (!pp) return PATHINFO_FAILED; if(safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); goto out; } pp->udev = udev_device_ref(udevice); err = pathinfo(pp, hwtable, (conf->cmd == CMD_REMOVE_WWID)? flag : (flag | DI_BLACKLIST)); if (err) goto out; err = store_path(pathvec, pp); if (err) goto out; out: if (err) free_path(pp); else if (pp_ptr) *pp_ptr = pp; return err; } static int path_discover (vector pathvec, struct config * conf, struct udev_device *udevice, int flag) { struct path * pp; const char * devname; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; if (filter_property(conf, udevice) > 0) return PATHINFO_SKIPPED; if (filter_devnode(conf->blist_devnode, conf->elist_devnode, (char *)devname) > 0) return PATHINFO_SKIPPED; pp = find_path_by_dev(pathvec, (char *)devname); if (!pp) { return store_pathinfo(pathvec, conf->hwtable, udevice, flag, NULL); } return pathinfo(pp, conf->hwtable, flag); } int path_discovery (vector pathvec, struct config * conf, int flag) { struct udev_enumerate *udev_iter; struct udev_list_entry *entry; struct udev_device *udevice; const char *devpath; int num_paths = 0, total_paths = 0; udev_iter = udev_enumerate_new(conf->udev); if (!udev_iter) return -ENOMEM; udev_enumerate_add_match_subsystem(udev_iter, "block"); udev_enumerate_scan_devices(udev_iter); udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_iter)) { const char *devtype; devpath = udev_list_entry_get_name(entry); condlog(4, "Discover device %s", devpath); udevice = udev_device_new_from_syspath(conf->udev, devpath); if (!udevice) { condlog(4, "%s: no udev information", devpath); continue; } devtype = udev_device_get_devtype(udevice); if(devtype && !strncmp(devtype, "disk", 4)) { total_paths++; if (path_discover(pathvec, conf, udevice, flag) == PATHINFO_OK) num_paths++; } udev_device_unref(udevice); } udev_enumerate_unref(udev_iter); condlog(4, "Discovered %d/%d paths", num_paths, total_paths); return (total_paths - num_paths); } #define declare_sysfs_get_str(fname) \ extern ssize_t \ sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \ { \ int l; \ const char * attr; \ const char * devname; \ \ if (!udev) \ return -ENOSYS; \ \ devname = udev_device_get_sysname(udev); \ \ attr = udev_device_get_sysattr_value(udev, #fname); \ if (!attr) { \ condlog(3, "%s: attribute %s not found in sysfs", \ devname, #fname); \ return -ENXIO; \ } \ for (l = strlen(attr); l >= 1 && isspace(attr[l-1]); l--); \ if (l > len) { \ condlog(3, "%s: overflow in attribute %s", \ devname, #fname); \ return -EINVAL; \ } \ strlcpy(buff, attr, len); \ return strchop(buff); \ } declare_sysfs_get_str(devtype); declare_sysfs_get_str(vendor); declare_sysfs_get_str(model); declare_sysfs_get_str(rev); ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff, size_t len) { ssize_t attr_len; char attrname[9]; const char * devname; if (!udev) { condlog(3, "No udev device given\n"); return -ENOSYS; } devname = udev_device_get_sysname(udev); sprintf(attrname, "vpd_pg%02x", pg); attr_len = sysfs_bin_attr_get_value(udev, attrname, buff, len); if (attr_len < 0) { condlog(3, "%s: attribute %s not found in sysfs", devname, attrname); return attr_len; } return attr_len; } int sysfs_get_timeout(struct path *pp, unsigned int *timeout) { const char *attr = NULL; const char *subsys; struct udev_device *parent; int r; unsigned int t; if (!pp->udev || pp->bus != SYSFS_BUS_SCSI) return -ENOSYS; parent = pp->udev; while (parent) { subsys = udev_device_get_subsystem(parent); attr = udev_device_get_sysattr_value(parent, "timeout"); if (subsys && attr) break; parent = udev_device_get_parent(parent); } if (!attr) { condlog(3, "%s: No timeout value in sysfs", pp->dev); return -ENXIO; } r = sscanf(attr, "%u\n", &t); if (r != 1) { condlog(3, "%s: Cannot parse timeout attribute '%s'", pp->dev, attr); return -EINVAL; } *timeout = t; return 0; } int sysfs_get_tgt_nodename (struct path *pp, char * node) { const char *tgtname, *value; struct udev_device *parent, *tgtdev; int host, channel, tgtid = -1; parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_device"); if (!parent) return 1; /* Check for SAS */ value = udev_device_get_sysattr_value(parent, "sas_address"); if (value) { tgtdev = udev_device_get_parent(parent); while (tgtdev) { tgtname = udev_device_get_sysname(tgtdev); if (sscanf(tgtname, "end_device-%d:%d", &host, &tgtid) == 2) break; tgtdev = udev_device_get_parent(tgtdev); tgtid = -1; } if (tgtid >= 0) { pp->sg_id.proto_id = SCSI_PROTOCOL_SAS; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); return 0; } } /* Check for USB */ tgtdev = udev_device_get_parent(parent); while (tgtdev) { value = udev_device_get_subsystem(tgtdev); if (value && !strcmp(value, "usb")) { pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; tgtname = udev_device_get_sysname(tgtdev); strncpy(node, tgtname, strlen(tgtname)); condlog(3, "%s: skip USB device %s", pp->dev, node); return 1; } tgtdev = udev_device_get_parent(tgtdev); } parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target"); if (!parent) return 1; /* Check for FibreChannel */ tgtdev = udev_device_get_parent(parent); value = udev_device_get_sysname(tgtdev); if (sscanf(value, "rport-%d:%d-%d", &host, &channel, &tgtid) == 3) { tgtdev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_remote_ports", value); if (tgtdev) { condlog(3, "SCSI target %d:%d:%d -> " "FC rport %d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, host, channel, tgtid); value = udev_device_get_sysattr_value(tgtdev, "node_name"); if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_FCP; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } else udev_device_unref(tgtdev); } } /* Check for iSCSI */ parent = pp->udev; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); if (tgtname && sscanf(tgtname , "session%d", &tgtid) == 1) break; parent = udev_device_get_parent(parent); tgtname = NULL; tgtid = -1; } if (parent && tgtname) { tgtdev = udev_device_new_from_subsystem_sysname(conf->udev, "iscsi_session", tgtname); if (tgtdev) { const char *value; value = udev_device_get_sysattr_value(tgtdev, "targetname"); if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } else udev_device_unref(tgtdev); } } /* Check for libata */ parent = pp->udev; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); if (tgtname && sscanf(tgtname, "ata%d", &tgtid) == 1) break; parent = udev_device_get_parent(parent); tgtname = NULL; } if (tgtname) { pp->sg_id.proto_id = SCSI_PROTOCOL_ATA; pp->sg_id.transport_id = tgtid; snprintf(node, NODE_NAME_SIZE, "ata-%d.00", tgtid); return 0; } /* Unknown SCSI transport. Keep fingers crossed */ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; return 0; } int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name) { int proto_id; if (!pp || !adapter_name) return 1; proto_id = pp->sg_id.proto_id; if (proto_id != SCSI_PROTOCOL_FCP && proto_id != SCSI_PROTOCOL_SAS && proto_id != SCSI_PROTOCOL_ISCSI && proto_id != SCSI_PROTOCOL_SRP) { return 1; } /* iscsi doesn't have adapter info in sysfs * get ip_address for grouping paths */ if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) return sysfs_get_iscsi_ip_address(pp, adapter_name); /* fetch adapter pci name for other protocols */ return sysfs_get_host_pci_name(pp, adapter_name); } int sysfs_get_host_pci_name(struct path *pp, char *pci_name) { struct udev_device *hostdev, *parent; char host_name[HOST_NAME_LEN]; const char *driver_name, *value; if (!pp || !pci_name) return 1; sprintf(host_name, "host%d", pp->sg_id.host_no); hostdev = udev_device_new_from_subsystem_sysname(conf->udev, "scsi_host", host_name); if (!hostdev) return 1; parent = udev_device_get_parent(hostdev); while (parent) { driver_name = udev_device_get_driver(parent); if (!driver_name) { parent = udev_device_get_parent(parent); continue; } if (!strcmp(driver_name, "pcieport")) break; parent = udev_device_get_parent(parent); } if (parent) { /* pci_device found */ value = udev_device_get_sysname(parent); strncpy(pci_name, value, SLOT_NAME_SIZE); udev_device_unref(hostdev); return 0; } udev_device_unref(hostdev); return 1; } int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address) { struct udev_device *hostdev; char host_name[HOST_NAME_LEN]; const char *value; sprintf(host_name, "host%d", pp->sg_id.host_no); hostdev = udev_device_new_from_subsystem_sysname(conf->udev, "iscsi_host", host_name); if (hostdev) { value = udev_device_get_sysattr_value(hostdev, "ipaddress"); if (value) { strncpy(ip_address, value, SLOT_NAME_SIZE); udev_device_unref(hostdev); return 0; } else udev_device_unref(hostdev); } return 1; } static void sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *rport_dev = NULL; char value[16]; char rport_id[32]; unsigned long long tmo = 0; int ret; sprintf(rport_id, "rport-%d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); rport_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_remote_ports", rport_id); if (!rport_dev) { condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev, rport_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, rport_id); /* * This is tricky. * dev_loss_tmo will be limited to 600 if fast_io_fail * is _not_ set. * fast_io_fail will be limited by the current dev_loss_tmo * setting. * So to get everything right we first need to increase * dev_loss_tmo to the fast_io_fail setting (if present), * then set fast_io_fail, and _then_ set dev_loss_tmo * to the correct value. */ memset(value, 0, 16); if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET && mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO && mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) { /* Check if we need to temporarily increase dev_loss_tmo */ ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo", value, 16); if (ret <= 0) { condlog(0, "%s: failed to read dev_loss_tmo value, " "error %d", rport_id, -ret); goto out; } if (sscanf(value, "%llu\n", &tmo) != 1) { condlog(0, "%s: Cannot parse dev_loss_tmo " "attribute '%s'", rport_id, value); goto out; } if (mpp->fast_io_fail >= tmo) { snprintf(value, 16, "%u", mpp->fast_io_fail + 1); } } else if (mpp->dev_loss > 600) { condlog(3, "%s: limiting dev_loss_tmo to 600, since " "fast_io_fail is not set", rport_id); snprintf(value, 16, "%u", 600); } else { snprintf(value, 16, "%u", mpp->dev_loss); } if (strlen(value)) { ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d", rport_id, value, -ret); goto out; } } if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) sprintf(value, "off"); else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) sprintf(value, "0"); else snprintf(value, 16, "%u", mpp->fast_io_fail); ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set fast_io_fail_tmo to %s, error %d", rport_id, value, -ret); } } if (tmo > 0) { snprintf(value, 16, "%u", mpp->dev_loss); ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d", rport_id, value, -ret); } } out: udev_device_unref(rport_dev); } static void sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *session_dev = NULL; char session_id[64]; char value[11]; sprintf(session_id, "session%d", pp->sg_id.transport_id); session_dev = udev_device_new_from_subsystem_sysname(conf->udev, "iscsi_session", session_id); if (!session_dev) { condlog(1, "%s: No iscsi session for '%s'", pp->dev, session_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, session_id); if (mpp->dev_loss) { condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev); } if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) { condlog(3, "%s: can't switch off fast_io_fail_tmo " "on iSCSI", pp->dev); } else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) { condlog(3, "%s: can't set fast_io_fail_tmo to '0'" "on iSCSI", pp->dev); } else { snprintf(value, 11, "%u", mpp->fast_io_fail); if (sysfs_attr_set_value(session_dev, "recovery_tmo", value, 11) <= 0) { condlog(3, "%s: Failed to set recovery_tmo, " " error %d", pp->dev, errno); } } } udev_device_unref(session_dev); return; } static void sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *sas_dev = NULL; char end_dev_id[64]; char value[11]; sprintf(end_dev_id, "end_device-%d:%d", pp->sg_id.host_no, pp->sg_id.transport_id); sas_dev = udev_device_new_from_subsystem_sysname(conf->udev, "sas_end_device", end_dev_id); if (!sas_dev) { condlog(1, "%s: No SAS end device for '%s'", pp->dev, end_dev_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id); if (mpp->dev_loss) { snprintf(value, 11, "%u", mpp->dev_loss); if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout", value, 11) <= 0) condlog(3, "%s: failed to update " "I_T Nexus loss timeout, error %d", pp->dev, errno); } udev_device_unref(sas_dev); return; } int sysfs_set_scsi_tmo (struct multipath *mpp) { struct path *pp; int i; int dev_loss_tmo = mpp->dev_loss; if (mpp->no_path_retry > 0) { int no_path_retry_tmo = mpp->no_path_retry * conf->checkint; if (no_path_retry_tmo > MAX_DEV_LOSS_TMO) no_path_retry_tmo = MAX_DEV_LOSS_TMO; if (no_path_retry_tmo > dev_loss_tmo) dev_loss_tmo = no_path_retry_tmo; condlog(3, "%s: update dev_loss_tmo to %d", mpp->alias, dev_loss_tmo); } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) { dev_loss_tmo = MAX_DEV_LOSS_TMO; condlog(3, "%s: update dev_loss_tmo to %d", mpp->alias, dev_loss_tmo); } mpp->dev_loss = dev_loss_tmo; if (mpp->dev_loss && mpp->fast_io_fail >= (int)mpp->dev_loss) { condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)", mpp->alias, mpp->fast_io_fail); mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF; } if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) return 0; vector_foreach_slot(mpp->paths, pp, i) { if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) sysfs_set_rport_tmo(mpp, pp); if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) sysfs_set_session_tmo(mpp, pp); if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS) sysfs_set_nexus_loss_tmo(mpp, pp); } return 0; } int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; if (cmddt) inqCmdBlk[1] |= 2; if (evpd) inqCmdBlk[1] |= 1; inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return -1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if(RECOVERED_ERROR == sense_key) return 0; } } return -1; } static int get_serial (char * str, int maxlen, int fd) { int len = 0; char buff[MX_ALLOC_LEN + 1] = {0}; if (fd < 0) return 1; if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN)) { len = buff[3]; if (len >= maxlen) return 1; if (len > 0) { memcpy(str, buff + 4, len); str[len] = '\0'; } return 0; } return 1; } #define DEFAULT_SGIO_LEN 254 static int sgio_get_vpd (unsigned char * buff, int maxlen, int fd) { int len = DEFAULT_SGIO_LEN; if (fd < 0) { errno = EBADF; return -1; } retry: if (0 == do_inq(fd, 0, 1, 0x83, buff, len)) { len = buff[3] + (buff[2] << 8); if (len >= maxlen) return len; if (len > DEFAULT_SGIO_LEN) goto retry; return 0; } return -1; } static int get_geometry(struct path *pp) { if (pp->fd < 0) return 1; if (ioctl(pp->fd, HDIO_GETGEO, &pp->geom)) { condlog(2, "%s: HDIO_GETGEO failed with %d", pp->dev, errno); memset(&pp->geom, 0, sizeof(pp->geom)); return 1; } condlog(3, "%s: %u cyl, %u heads, %u sectors/track, start at %lu", pp->dev, pp->geom.cylinders, pp->geom.heads, pp->geom.sectors, pp->geom.start); return 0; } static int parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len) { char *p = NULL; int len = in[3] + (in[2] << 8); if (len >= out_len) { condlog(2, "vpd pg80 overflow, %d/%d bytes required", len, (int)out_len); len = out_len; } if (len > 0) { memcpy(out, in + 4, len); out[len] = '\0'; } /* * Strip trailing whitspaces */ p = out + len - 1; while (p > out && *p == ' ') { *p = '\0'; p--; len --; } return len; } static int parse_vpd_pg83(const unsigned char *in, size_t in_len, char *out, size_t out_len) { unsigned char *d; unsigned char *vpd = NULL; int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio; d = (unsigned char *)in + 4; while (d < (unsigned char *)in + in_len) { /* Select 'association: LUN' */ if ((d[1] & 0x30) != 0) { d += d[3] + 4; continue; } switch (d[1] & 0xf) { case 0x3: /* NAA: Prio 5 */ switch (d[4] >> 4) { case 6: /* IEEE Registered Extended: Prio 8 */ naa_prio = 8; break; case 5: /* IEEE Registered: Prio 7 */ naa_prio = 7; break; case 2: /* IEEE Extended: Prio 6 */ naa_prio = 6; break; case 3: /* IEEE Locally assigned: Prio 1 */ naa_prio = 1; break; default: /* Default: no priority */ naa_prio = -1; break; } if (prio < naa_prio) { prio = naa_prio; vpd = d; } break; case 0x8: /* SCSI Name: Prio 4 */ if (memcmp(d + 4, "eui.", 4) && memcmp(d + 4, "naa.", 4) && memcmp(d + 4, "iqn.", 4)) continue; if (prio < 4) { prio = 4; vpd = d; } break; case 0x2: /* EUI-64: Prio 3 */ if (prio < 3) { prio = 3; vpd = d; } break; case 0x1: /* T-10 Vendor ID: Prio 2 */ if (prio < 2) { prio = 2; vpd = d; } break; } d += d[3] + 4; } if (prio > 0) { vpd_type = vpd[1] & 0xf; vpd_len = vpd[3]; vpd += 4; if (vpd_type == 0x2 || vpd_type == 0x3) { int i; len = sprintf(out, "%d", vpd_type); for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%02x", vpd[i]); if (len >= out_len) break; } } else if (vpd_type == 0x8) { if (!memcmp("eui.", vpd, 4)) { out[0] = '2'; len = 1; vpd += 4; vpd_len -= 4; for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%c", tolower(vpd[i])); if (len >= out_len) break; } len = vpd_len + 1; out[len] = '\0'; } else if (!memcmp("naa.", vpd, 4)) { out[0] = '3'; len = 1; vpd += 4; vpd_len -= 4; for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%c", tolower(vpd[i])); if (len >= out_len) break; } len = vpd_len + 1; out[len] = '\0'; } else { out[0] = '8'; len = 1; vpd += 4; vpd_len -= 4; if (vpd_len > out_len + 2) vpd_len = out_len - 2; memcpy(out, vpd, vpd_len); len = vpd_len + 1; out[len] = '\0'; } } else if (vpd_type == 0x1) { unsigned char *p; int p_len; out[0] = '1'; len = 1; p = vpd; while ((p = memchr(vpd, ' ', vpd_len))) { p_len = p - vpd; if (len + p_len > out_len - 1) p_len = out_len - len - 2; memcpy(out + len, vpd, p_len); len += p_len; if (len >= out_len - 1) { out[len] = '\0'; break; } out[len] = '_'; len ++; vpd = p; vpd_len -= p_len; while (vpd && *vpd == ' ') { vpd++; vpd_len --; } } if (len > 1 && out[len - 1] == '_') { out[len - 1] = '\0'; len--; } } } return len; } static int get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) { int len, buff_len; unsigned char buff[4096]; memset(buff, 0x0, 4096); if (!parent || sysfs_get_vpd(parent, pg, buff, 4096) <= 0) { condlog(3, "failed to read sysfs vpd pg%02x", pg); return -EINVAL; } if (buff[1] != pg) { condlog(3, "vpd pg%02x error, invalid vpd page %02x", pg, buff[1]); return -ENODATA; } buff_len = (buff[2] << 8) + buff[3] + 4; if (buff_len > 4096) condlog(3, "vpd pg%02x page truncated", pg); if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); else len = -ENOSYS; return len; } static int get_vpd_sgio (int fd, int pg, char * str, int maxlen) { int len, buff_len; unsigned char buff[4096]; memset(buff, 0x0, 4096); if (sgio_get_vpd(buff, 4096, fd) <= 0) { condlog(3, "failed to issue vpd inquiry for pg%02x", pg); return -errno; } if (buff[1] != pg) { condlog(3, "vpd pg%02x error, invalid vpd page %02x", pg, buff[1]); return -ENODATA; } buff_len = (buff[2] << 8) + buff[3] + 4; if (buff_len > 4096) condlog(3, "vpd pg%02x page truncated", pg); if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); else len = -ENOSYS; return len; } static int scsi_sysfs_pathinfo (struct path * pp) { struct udev_device *parent; const char *attr_path = NULL; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id, &pp->sg_id.lun) == 4) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 1; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); /* * host / bus / target / lun */ condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); /* * target node name */ if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) return 1; condlog(3, "%s: tgt_node_name = %s", pp->dev, pp->tgt_node_name); return 0; } static int ccw_sysfs_pathinfo (struct path * pp) { struct udev_device *parent; char attr_buff[NAME_SIZE]; const char *attr_path; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "ccw", 3)) break; parent = udev_device_get_parent(parent); } if (!parent) return 1; sprintf(pp->vendor_id, "IBM"); condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE) <= 0) return 1; if (!strncmp(attr_buff, "3370", 4)) { sprintf(pp->product_id,"S/390 DASD FBA"); } else if (!strncmp(attr_buff, "9336", 4)) { sprintf(pp->product_id,"S/390 DASD FBA"); } else { sprintf(pp->product_id,"S/390 DASD ECKD"); } condlog(3, "%s: product = %s", pp->dev, pp->product_id); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, NULL); /* * host / bus / target / lun */ attr_path = udev_device_get_sysname(parent); pp->sg_id.lun = 0; sscanf(attr_path, "%i.%i.%x", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id); condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); return 0; } static int cciss_sysfs_pathinfo (struct path * pp) { const char * attr_path = NULL; struct udev_device *parent; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "cciss", 5)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "c%id%i", &pp->sg_id.host_no, &pp->sg_id.scsi_id) == 2) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 1; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); /* * host / bus / target / lun */ pp->sg_id.lun = 0; pp->sg_id.channel = 0; condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); return 0; } static int common_sysfs_pathinfo (struct path * pp) { dev_t devt; if (!pp) return 1; if (!pp->udev) { condlog(4, "%s: udev not initialised", pp->dev); return 1; } devt = udev_device_get_devnum(pp->udev); snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt)); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); if (sysfs_get_size(pp, &pp->size)) return 1; condlog(3, "%s: size = %llu", pp->dev, pp->size); return 0; } int path_offline (struct path * pp) { struct udev_device * parent; char buff[SCSI_STATE_SIZE]; int err; if (pp->bus != SYSFS_BUS_SCSI) return PATH_UP; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) break; parent = udev_device_get_parent(parent); } if (!parent) { condlog(1, "%s: failed to get sysfs information", pp->dev); return PATH_REMOVED; } memset(buff, 0x0, SCSI_STATE_SIZE); err = sysfs_attr_get_value(parent, "state", buff, SCSI_STATE_SIZE); if (err <= 0) { if (err == -ENXIO) return PATH_REMOVED; else return PATH_DOWN; } condlog(3, "%s: path state = %s", pp->dev, buff); if (!strncmp(buff, "offline", 7)) { pp->offline = 1; return PATH_DOWN; } pp->offline = 0; if (!strncmp(buff, "blocked", 7) || !strncmp(buff, "quiesce", 7)) return PATH_PENDING; else if (!strncmp(buff, "running", 7)) return PATH_UP; return PATH_DOWN; } int sysfs_pathinfo(struct path * pp) { if (common_sysfs_pathinfo(pp)) return 1; pp->bus = SYSFS_BUS_UNDEF; if (!strncmp(pp->dev,"cciss",5)) pp->bus = SYSFS_BUS_CCISS; if (!strncmp(pp->dev,"dasd", 4)) pp->bus = SYSFS_BUS_CCW; if (!strncmp(pp->dev,"sd", 2)) pp->bus = SYSFS_BUS_SCSI; if (pp->bus == SYSFS_BUS_UNDEF) return 0; else if (pp->bus == SYSFS_BUS_SCSI) { if (scsi_sysfs_pathinfo(pp)) return 1; } else if (pp->bus == SYSFS_BUS_CCW) { if (ccw_sysfs_pathinfo(pp)) return 1; } else if (pp->bus == SYSFS_BUS_CCISS) { if (cciss_sysfs_pathinfo(pp)) return 1; } return 0; } static int scsi_ioctl_pathinfo (struct path * pp, int mask) { struct udev_device *parent; const char *attr_path = NULL; if (!(mask & DI_SERIAL)) return 0; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id, &pp->sg_id.lun) == 4) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 0; if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) > 0) condlog(3, "%s: serial = %s", pp->dev, pp->serial); return 0; } static int cciss_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SERIAL) { get_serial(pp->serial, SERIAL_SIZE, pp->fd); condlog(3, "%s: serial = %s", pp->dev, pp->serial); } return 0; } int get_state (struct path * pp, int daemon) { struct checker * c = &pp->checker; int state; condlog(3, "%s: get_state", pp->dev); if (!checker_selected(c)) { if (daemon) { if (pathinfo(pp, conf->hwtable, DI_SYSFS) != PATHINFO_OK) { condlog(3, "%s: couldn't get sysfs pathinfo", pp->dev); return PATH_UNCHECKED; } } select_checker(pp); if (!checker_selected(c)) { condlog(3, "%s: No checker selected", pp->dev); return PATH_UNCHECKED; } checker_set_fd(c, pp->fd); if (checker_init(c, pp->mpp?&pp->mpp->mpcontext:NULL)) { memset(c, 0x0, sizeof(struct checker)); condlog(3, "%s: checker init failed", pp->dev); return PATH_UNCHECKED; } } checker_clear_message(c); if (daemon) { if (conf->force_sync == 0) checker_set_async(c); else checker_set_sync(c); } if (!conf->checker_timeout && sysfs_get_timeout(pp, &(c->timeout)) <= 0) c->timeout = DEF_TIMEOUT; state = checker_check(c); condlog(3, "%s: state = %s", pp->dev, checker_state_name(state)); if (state != PATH_UP && state != PATH_GHOST && strlen(checker_message(c))) condlog(3, "%s: checker msg is \"%s\"", pp->dev, checker_message(c)); return state; } static int get_prio (struct path * pp) { if (!pp) return 0; struct prio * p = &pp->prio; if (!prio_selected(p)) { select_detect_prio(pp); select_prio(pp); if (!prio_selected(p)) { condlog(3, "%s: no prio selected", pp->dev); pp->priority = PRIO_UNDEF; return 1; } } pp->priority = prio_getprio(p, pp); if (pp->priority < 0) { condlog(3, "%s: %s prio error", pp->dev, prio_name(p)); pp->priority = PRIO_UNDEF; return 1; } condlog(3, "%s: %s prio = %u", pp->dev, prio_name(p), pp->priority); return 0; } static int get_udev_uid(struct path * pp, char *uid_attribute) { ssize_t len; const char *value; value = udev_device_get_property_value(pp->udev, uid_attribute); if ((!value || strlen(value) == 0) && conf->cmd == CMD_VALID_PATH) value = getenv(uid_attribute); if (value && strlen(value)) { if (strlen(value) + 1 > WWID_SIZE) { condlog(0, "%s: wwid overflow", pp->dev); len = WWID_SIZE; } else { len = strlen(value); } strncpy(pp->wwid, value, len); } else { condlog(3, "%s: no %s attribute", pp->dev, uid_attribute); len = -EINVAL; } return len; } static int get_vpd_uid(struct path * pp) { struct udev_device *parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) break; parent = udev_device_get_parent(parent); } return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE); } static int get_uid (struct path * pp) { char *c; const char *origin = "unknown"; ssize_t len = 0; if (!pp->uid_attribute && !pp->getuid) select_getuid(pp); if (!pp->udev) { condlog(1, "%s: no udev information", pp->dev); return 1; } memset(pp->wwid, 0, WWID_SIZE); if (pp->getuid) { char buff[CALLOUT_MAX_SIZE]; /* Use 'getuid' callout, deprecated */ condlog(1, "%s: using deprecated getuid callout", pp->dev); if (apply_format(pp->getuid, &buff[0], pp)) { condlog(0, "error formatting uid callout command"); memset(pp->wwid, 0, WWID_SIZE); len = -EINVAL; } else if (execute_program(buff, pp->wwid, WWID_SIZE)) { condlog(3, "error calling out %s", buff); memset(pp->wwid, 0, WWID_SIZE); len = -EIO; } else len = strlen(pp->wwid); origin = "callout"; } else { if (pp->uid_attribute) { len = get_udev_uid(pp, pp->uid_attribute); origin = "udev"; if (len <= 0) condlog(1, "%s: failed to get udev uid: %s", pp->dev, strerror(-len)); } if (len <= 0 && !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) { len = get_vpd_uid(pp); origin = "sysfs"; pp->uid_attribute = NULL; if (len < 0) { condlog(1, "%s: failed to get sysfs uid: %s", pp->dev, strerror(-len)); len = get_vpd_sgio(pp->fd, 0x83, pp->wwid, WWID_SIZE); origin = "sgio"; } } } if ( len < 0 ) { condlog(1, "%s: failed to get %s uid: %s", pp->dev, origin, strerror(-len)); memset(pp->wwid, 0x0, WWID_SIZE); } else { /* Strip any trailing blanks */ c = strchr(pp->wwid, '\0'); c--; while (c && c >= pp->wwid && *c == ' ') { *c = '\0'; c--; } } condlog(3, "%s: uid = %s (%s)", pp->dev, *pp->wwid == '\0' ? "" : pp->wwid, origin); return 0; } extern int pathinfo (struct path *pp, vector hwtable, int mask) { int path_state; if (!pp) return PATHINFO_FAILED; condlog(3, "%s: mask = 0x%x", pp->dev, mask); /* * fetch info available in sysfs */ if (mask & DI_SYSFS && sysfs_pathinfo(pp)) return PATHINFO_FAILED; if (mask & DI_BLACKLIST && mask & DI_SYSFS) { if (filter_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id) > 0) { return PATHINFO_SKIPPED; } } path_state = path_offline(pp); if (path_state == PATH_REMOVED) goto blank; /* * fetch info not available through sysfs */ if (pp->fd < 0) pp->fd = open(udev_device_get_devnode(pp->udev), O_RDONLY); if (pp->fd < 0) { condlog(4, "Couldn't open node for %s: %s", pp->dev, strerror(errno)); goto blank; } if (mask & DI_SERIAL) get_geometry(pp); if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI && scsi_ioctl_pathinfo(pp, mask)) goto blank; if (pp->bus == SYSFS_BUS_CCISS && cciss_ioctl_pathinfo(pp, mask)) goto blank; if (mask & DI_CHECKER) { if (path_state == PATH_UP) { pp->chkrstate = pp->state = get_state(pp, 0); if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) goto blank; if (pp->state == PATH_TIMEOUT) pp->state = PATH_DOWN; if (pp->state == PATH_UP && !pp->size) { condlog(3, "%s: device size is 0, " "path unuseable", pp->dev); pp->state = PATH_GHOST; } } else { condlog(3, "%s: path inaccessible", pp->dev); pp->chkrstate = pp->state = path_state; if (path_state == PATH_PENDING || path_state == PATH_DOWN) pp->priority = 0; } } if ((mask & DI_WWID) && !strlen(pp->wwid)) get_uid(pp); if (mask & DI_BLACKLIST && mask & DI_WWID) { if (!strlen(pp->wwid) || filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid, pp->dev) > 0) { return PATHINFO_SKIPPED; } } /* * Retrieve path priority, even for PATH_DOWN paths if it has never * been successfully obtained before. */ if ((mask & DI_PRIO) && path_state == PATH_UP) { if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) { if (!strlen(pp->wwid)) get_uid(pp); if (!strlen(pp->wwid)) return PATHINFO_SKIPPED; get_prio(pp); } } pp->initialized = 1; return PATHINFO_OK; blank: /* * Recoverable error, for example faulty or offline path */ memset(pp->wwid, 0, WWID_SIZE); pp->chkrstate = pp->state = PATH_DOWN; pp->initialized = 0; return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/discovery.h000066400000000000000000000033401260771327300234270ustar00rootroot00000000000000#ifndef DISCOVERY_H #define DISCOVERY_H #define SYSFS_PATH_SIZE 255 #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define TUR_CMD_LEN 6 #ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) #endif #ifndef DEF_TIMEOUT #define DEF_TIMEOUT 30 #endif /* * exerpt from sg_err.h */ #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define PATHINFO_OK 0 #define PATHINFO_FAILED 1 #define PATHINFO_SKIPPED 2 struct config; int path_discovery (vector pathvec, struct config * conf, int flag); int do_tur (char *); int path_offline (struct path *); int get_state (struct path * pp, int daemon); int pathinfo (struct path *, vector hwtable, int mask); int alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr); int store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr); int sysfs_set_scsi_tmo (struct multipath *mpp); int sysfs_get_timeout(struct path *pp, unsigned int *timeout); int sysfs_get_host_pci_name(struct path *pp, char *pci_name); int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); /* * discovery bitmask */ enum discovery_mode { __DI_SYSFS, __DI_SERIAL, __DI_CHECKER, __DI_PRIO, __DI_WWID, __DI_BLACKLIST, }; #define DI_SYSFS (1 << __DI_SYSFS) #define DI_SERIAL (1 << __DI_SERIAL) #define DI_CHECKER (1 << __DI_CHECKER) #define DI_PRIO (1 << __DI_PRIO) #define DI_WWID (1 << __DI_WWID) #define DI_BLACKLIST (1 << __DI_BLACKLIST) #define DI_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \ DI_WWID) #endif /* DISCOVERY_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/dmparser.c000066400000000000000000000236351260771327300232410ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Stefan Bader, IBM * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "structs.h" #include "util.h" #include "debug.h" #include "config.h" #define WORD_SIZE 64 static int merge_words (char ** dst, char * word, int space) { char * p = *dst; int len; len = strlen(*dst) + strlen(word) + space; *dst = REALLOC(*dst, len + 1); if (!*dst) { free(p); return 1; } p = *dst; while (*p != '\0') p++; while (space) { *p = ' '; p++; space--; } strncpy(p, word, strlen(word) + 1); return 0; } /* * Transforms the path group vector into a proper device map string */ int assemble_map (struct multipath * mp, char * params, int len) { int i, j; int shift, freechar; int minio; int nr_priority_groups, initial_pg_nr; char * p, * f; char no_path_retry[] = "queue_if_no_path"; char retain_hwhandler[] = "retain_attached_hw_handler"; struct pathgroup * pgp; struct path * pp; minio = mp->minio; p = params; freechar = len; nr_priority_groups = VECTOR_SIZE(mp->pg); initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0); f = STRDUP(mp->features); /* * We have to set 'queue_if_no_path' here even * to avoid path failures during map reload. */ if (mp->no_path_retry == NO_PATH_RETRY_UNDEF || mp->no_path_retry == NO_PATH_RETRY_FAIL) { /* remove queue_if_no_path settings */ condlog(3, "%s: remove queue_if_no_path from '%s'", mp->alias, mp->features); remove_feature(&f, no_path_retry); } else { add_feature(&f, no_path_retry); } if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON) add_feature(&f, retain_hwhandler); shift = snprintf(p, freechar, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups, initial_pg_nr); FREE(f); if (shift >= freechar) { condlog(0, "%s: params too small", mp->alias); return 1; } p += shift; freechar -= shift; vector_foreach_slot (mp->pg, pgp, i) { pgp = VECTOR_SLOT(mp->pg, i); shift = snprintf(p, freechar, " %s %i 1", mp->selector, VECTOR_SIZE(pgp->paths)); if (shift >= freechar) { condlog(0, "%s: params too small", mp->alias); return 1; } p += shift; freechar -= shift; vector_foreach_slot (pgp->paths, pp, j) { int tmp_minio = minio; if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) tmp_minio = minio * pp->priority; if (!strlen(pp->dev_t) ) { condlog(0, "dev_t not set for '%s'", pp->dev); return 1; } shift = snprintf(p, freechar, " %s %d", pp->dev_t, tmp_minio); if (shift >= freechar) { condlog(0, "%s: params too small", mp->alias); return 1; } p += shift; freechar -= shift; } } if (freechar < 1) { condlog(0, "%s: params too small", mp->alias); return 1; } snprintf(p, 1, "\n"); condlog(3, "%s: assembled map [%s]", mp->alias, params); return 0; } extern int disassemble_map (vector pathvec, char * params, struct multipath * mpp) { char * word; char * p; int i, j, k; int num_features = 0; int num_hwhandler = 0; int num_pg = 0; int num_pg_args = 0; int num_paths = 0; int num_paths_args = 0; int def_minio = 0; int no_path_retry = NO_PATH_RETRY_UNDEF; struct path * pp; struct pathgroup * pgp; p = params; condlog(3, "%s: disassemble map [%s]", mpp->alias, params); /* * features */ p += get_word(p, &mpp->features); if (!mpp->features) return 1; num_features = atoi(mpp->features); no_path_retry = mpp->no_path_retry; mpp->no_path_retry = NO_PATH_RETRY_UNDEF; for (i = 0; i < num_features; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->features, word, 1)) { FREE(word); return 1; } if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF) || (mpp->no_path_retry == NO_PATH_RETRY_FAIL) || (mpp->no_path_retry == NO_PATH_RETRY_QUEUE)) setup_feature(mpp, word); FREE(word); } /* * Reset no_path_retry. * - if not set from features * - if queue_if_no_path is set from features but * no_path_retry > 0 is selected. */ if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF || mpp->no_path_retry == NO_PATH_RETRY_QUEUE) && mpp->no_path_retry != no_path_retry) mpp->no_path_retry = no_path_retry; /* * hwhandler */ p += get_word(p, &mpp->hwhandler); if (!mpp->hwhandler) return 1; num_hwhandler = atoi(mpp->hwhandler); for (i = 0; i < num_hwhandler; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->hwhandler, word, 1)) { FREE(word); return 1; } FREE(word); } /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg > 0) { if (!mpp->pg) { mpp->pg = vector_alloc(); if (!mpp->pg) return 1; } } else { free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; } /* * first pg to try */ p += get_word(p, &word); if (!word) goto out; mpp->nextpg = atoi(word); FREE(word); for (i = 0; i < num_pg; i++) { /* * selector */ if (!mpp->selector) { p += get_word(p, &mpp->selector); if (!mpp->selector) goto out; /* * selector args */ p += get_word(p, &word); if (!word) goto out; num_pg_args = atoi(word); if (merge_words(&mpp->selector, word, 1)) { goto out1; } FREE(word); } else { p += get_word(p, NULL); p += get_word(p, NULL); } for (j = 0; j < num_pg_args; j++) p += get_word(p, NULL); /* * paths */ pgp = alloc_pathgroup(); if (!pgp) goto out; if (store_pathgroup(mpp->pg, pgp)) goto out; p += get_word(p, &word); if (!word) goto out; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) goto out; num_paths_args = atoi(word); FREE(word); for (j = 0; j < num_paths; j++) { pp = NULL; p += get_word(p, &word); if (!word) goto out; if (pathvec) pp = find_path_by_devt(pathvec, word); if (!pp) { pp = alloc_path(); if (!pp) goto out1; strncpy(pp->dev_t, word, BLK_DEV_SIZE); /* Only call this in multipath client mode */ if (!conf->daemon && store_path(pathvec, pp)) goto out1; } FREE(word); if (store_path(pgp->paths, pp)) goto out; /* * Update wwid for multipaths which are not setup * in the get_dm_mpvec() code path */ if (!strlen(mpp->wwid)) strncpy(mpp->wwid, pp->wwid, WWID_SIZE); /* * Update wwid for paths which may not have been * active at the time the getuid callout was run */ else if (!strlen(pp->wwid)) strncpy(pp->wwid, mpp->wwid, WWID_SIZE); pgp->id ^= (long)pp; pp->pgindex = i + 1; for (k = 0; k < num_paths_args; k++) if (k == 0) { if (!strncmp(mpp->selector, "round-robin", 11)) { p += get_word(p, &word); def_minio = atoi(word); if (mpp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) def_minio /= pp->priority; FREE(word); } else { p += get_word(p, NULL); def_minio = 0; } if (def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } return 0; out1: FREE(word); out: free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; return 1; } extern int disassemble_status (char * params, struct multipath * mpp) { char * word; char * p; int i, j, k; int num_feature_args; int num_hwhandler_args; int num_pg; int num_pg_args; int num_paths; int def_minio = 0; struct path * pp; struct pathgroup * pgp; p = params; condlog(3, "%s: disassemble status [%s]", mpp->alias, params); /* * features */ p += get_word(p, &word); if (!word) return 1; num_feature_args = atoi(word); FREE(word); for (i = 0; i < num_feature_args; i++) { if (i == 1) { p += get_word(p, &word); if (!word) return 1; mpp->queuedio = atoi(word); FREE(word); continue; } /* unknown */ p += get_word(p, NULL); } /* * hwhandler */ p += get_word(p, &word); if (!word) return 1; num_hwhandler_args = atoi(word); FREE(word); for (i = 0; i < num_hwhandler_args; i++) p += get_word(p, NULL); /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg == 0) return 0; /* * next pg to try */ p += get_word(p, NULL); if (VECTOR_SIZE(mpp->pg) < num_pg) return 1; for (i = 0; i < num_pg; i++) { pgp = VECTOR_SLOT(mpp->pg, i); /* * PG status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'D': pgp->status = PGSTATE_DISABLED; break; case 'A': pgp->status = PGSTATE_ACTIVE; break; case 'E': pgp->status = PGSTATE_ENABLED; break; default: pgp->status = PGSTATE_UNDEF; break; } FREE(word); /* * PG Status (discarded, would be '0' anyway) */ p += get_word(p, NULL); p += get_word(p, &word); if (!word) return 1; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) return 1; num_pg_args = atoi(word); FREE(word); if (VECTOR_SIZE(pgp->paths) < num_paths) return 1; for (j = 0; j < num_paths; j++) { pp = VECTOR_SLOT(pgp->paths, j); /* * path */ p += get_word(p, NULL); /* * path status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'F': pp->dmstate = PSTATE_FAILED; break; case 'A': pp->dmstate = PSTATE_ACTIVE; break; default: break; } FREE(word); /* * fail count */ p += get_word(p, &word); if (!word) return 1; pp->failcount = atoi(word); FREE(word); /* * selector args */ for (k = 0; k < num_pg_args; k++) { if (!strncmp(mpp->selector, "least-pending", 13)) { p += get_word(p, &word); if (sscanf(word,"%d:*d", &def_minio) == 1 && def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } } return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/dmparser.h000066400000000000000000000002431260771327300232340ustar00rootroot00000000000000int assemble_map (struct multipath *, char *, int); int disassemble_map (vector, char *, struct multipath *); int disassemble_status (char *, struct multipath *); multipath-tools-0.5.0+git1.656f8865/libmultipath/file.c000066400000000000000000000075671260771327300223510ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include #include #include #include #include "file.h" #include "debug.h" #include "uxsock.h" /* * significant parts of this file were taken from iscsi-bindings.c of the * linux-iscsi project. * Copyright (C) 2002 Cisco Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 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. * * See the file COPYING included with this distribution for more details. */ static int ensure_directories_exist(char *str, mode_t dir_mode) { char *pathname; char *end; int err; pathname = strdup(str); if (!pathname){ condlog(0, "Cannot copy file pathname %s : %s", str, strerror(errno)); return -1; } end = pathname; /* skip leading slashes */ while (end && *end && (*end == '/')) end++; while ((end = strchr(end, '/'))) { /* if there is another slash, make the dir. */ *end = '\0'; err = mkdir(pathname, dir_mode); if (err && errno != EEXIST) { condlog(0, "Cannot make directory [%s] : %s", pathname, strerror(errno)); free(pathname); return -1; } if (!err) condlog(3, "Created dir [%s]", pathname); *end = '/'; end++; } free(pathname); return 0; } static void sigalrm(int sig) { /* do nothing */ } static int lock_file(int fd, char *file_name) { struct sigaction act, oldact; sigset_t set, oldset; struct flock lock; int err; memset(&lock, 0, sizeof(lock)); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; act.sa_handler = sigalrm; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigemptyset(&set); sigaddset(&set, SIGALRM); sigaction(SIGALRM, &act, &oldact); pthread_sigmask(SIG_UNBLOCK, &set, &oldset); alarm(FILE_TIMEOUT); err = fcntl(fd, F_SETLKW, &lock); alarm(0); if (err) { if (errno != EINTR) condlog(0, "Cannot lock %s : %s", file_name, strerror(errno)); else condlog(0, "%s is locked. Giving up.", file_name); } pthread_sigmask(SIG_SETMASK, &oldset, NULL); sigaction(SIGALRM, &oldact, NULL); return err; } int open_file(char *file, int *can_write, char *header) { int fd; struct stat s; if (ensure_directories_exist(file, 0700)) return -1; *can_write = 1; fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { if (errno == EROFS) { *can_write = 0; condlog(3, "Cannot open file [%s] read/write. " " trying readonly", file); fd = open(file, O_RDONLY); if (fd < 0) { condlog(0, "Cannot open file [%s] " "readonly : %s", file, strerror(errno)); return -1; } } else { condlog(0, "Cannot open file [%s] : %s", file, strerror(errno)); return -1; } } if (*can_write && lock_file(fd, file) < 0) goto fail; memset(&s, 0, sizeof(s)); if (fstat(fd, &s) < 0){ condlog(0, "Cannot stat file %s : %s", file, strerror(errno)); goto fail; } if (s.st_size == 0) { if (*can_write == 0) goto fail; /* If file is empty, write the header */ size_t len = strlen(header); if (write_all(fd, header, len) != len) { condlog(0, "Cannot write header to file %s : %s", file, strerror(errno)); /* cleanup partially written header */ if (ftruncate(fd, 0)) condlog(0, "Cannot truncate header : %s", strerror(errno)); goto fail; } fsync(fd); condlog(3, "Initialized new file [%s]", file); } return fd; fail: close(fd); return -1; } multipath-tools-0.5.0+git1.656f8865/libmultipath/file.h000066400000000000000000000003011260771327300223310ustar00rootroot00000000000000/* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ #ifndef _FILE_H #define _FILE_H #define FILE_TIMEOUT 30 int open_file(char *file, int *can_write, char *header); #endif /* _FILE_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/hwtable.c000066400000000000000000000772151260771327300230550ustar00rootroot00000000000000#include #include "checkers.h" #include "vector.h" #include "defaults.h" #include "structs.h" #include "config.h" #include "pgpolicies.h" #include "prio.h" /* * Tuning suggestions on these parameters should go to * dm-devel@redhat.com * * You are welcome to claim maintainership over a controller * family. Please mail the currently enlisted maintainer and * the upstream package maintainer. */ static struct hwentry default_hw[] = { /* * Compellent Technologies, Inc. * * Maintainer : Jim Lester, Compellent * Mail : jim.lester@compellent.com */ { .vendor = "COMPELNT", .product = "Compellent Vol", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Apple controller family * * Maintainer : Shyam Sundar * Mail : g.shyamsundar@yahoo.co.in */ { .vendor = "APPLE*", .product = "Xserve RAID ", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DEFAULT_CHECKER, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * StorageWorks controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "3PARdata", .product = "VV", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DEFAULT_CHECKER, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "DEC", .product = "HSG80", .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = HP_SW, .prio_name = PRIO_HP_SW, .prio_args = NULL, }, { .vendor = "HP", .product = "A6189A", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ .vendor = "(COMPAQ|HP)", .product = "(MSA|HSV)1.0.*", .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = 100, .checker_name = HP_SW, .prio_name = PRIO_HP_SW, .prio_args = NULL, }, { /* MSA 1000/1500 with new firmware */ .vendor = "(COMPAQ|HP)", .product = "MSA VOLUME", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* EVA 3000/5000 with new firmware, EVA 4000/6000/8000 */ .vendor = "(COMPAQ|HP)", .product = "HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* HP MSA2000 product family with old firmware */ .vendor = "HP", .product = "MSA2[02]12fc|MSA2012i", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 18, .minio = 100, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* HP MSA2000 product family with new firmware */ .vendor = "HP", .product = "MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 18, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* HP SVSP */ .vendor = "HP", .product = "HSVX700", .features = DEFAULT_FEATURES, .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* HP Smart Array */ .vendor = "HP", .product = "LOGICAL VOLUME.*", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* HP P2000 family arrays */ .vendor = "HP", .product = "P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 18, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * DDN controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "DDN", .product = "SAN DataDirector", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * EMC / Clariion controller family * * Maintainer : Edward Goggin, EMC * Mail : egoggin@emc.com */ { .vendor = "EMC", .product = "SYMMETRIX", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 6, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "DGC", .product = ".*", .bl_product = "LUNZ", .features = "1 queue_if_no_path", .hwhandler = "1 emc", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .checker_name = EMC_CLARIION, .prio_name = PRIO_EMC, .prio_args = NULL, .retain_hwhandler = RETAIN_HWHANDLER_ON, .detect_prio = DETECT_PRIO_ON, }, { .vendor = "EMC", .product = "Invista", .bl_product = "LUNZ", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 5, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "XtremIO", .product = "XtremApp", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = "queue-length 0", .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .checker_name = TUR, .fast_io_fail = 5, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Fujitsu controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "FSC", .product = "CentricStor", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "FUJITSU", .product = "ETERNUS_DX(H|L|M|400|8000)", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 10, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * Hitachi controller family * * Maintainer : Matthias Rudolph * Mail : matthias.rudolph@hds.com */ { .vendor = "(HITACHI|HP)", .product = "OPEN-.*", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "HITACHI", .product = "DF.*", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_HDS, .prio_args = NULL, }, /* * IBM controller family * * Maintainer : Hannes Reinecke, SuSE * Mail : hare@suse.de */ { .vendor = "IBM", .product = "ProFibre 4000R", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM FAStT 1722-600 */ .vendor = "IBM", .product = "^1722-600", .bl_product = "Universal Xport", .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4100 */ .vendor = "IBM", .product = "^1724", .bl_product = "Universal Xport", .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS3200 / DS3300 / DS3400 */ .vendor = "IBM", .product = "^1726", .bl_product = "Universal Xport", .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4400 / DS4500 / FAStT700 */ .vendor = "IBM", .product = "^1742", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "IBM", .product = "^1745|^1746", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4700 */ .vendor = "IBM", .product = "^1814", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4800 */ .vendor = "IBM", .product = "^1815", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS5000 */ .vendor = "IBM", .product = "^1818", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM Netfinity Fibre Channel RAID Controller Unit */ .vendor = "IBM", .product = "^3526", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4200 / FAStT200 */ .vendor = "IBM", .product = "^3542", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", .product = "^2105800", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", .product = "^2105F20", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM DS6000 */ .vendor = "IBM", .product = "^1750500", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* IBM DS8000 */ .vendor = "IBM", .product = "^2107900", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM SAN Volume Controller */ .vendor = "IBM", .product = "^2145", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* IBM S/390 ECKD DASD */ .vendor = "IBM", .product = "S/390 DASD ECKD", .bl_product = "S/390.*", .uid_attribute = "ID_UID", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM S/390 FBA DASD */ .vendor = "IBM", .product = "S/390 DASD FBA", .bl_product = "S/390.*", .uid_attribute = "ID_UID", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM IPR */ .vendor = "IBM", .product = "^IPR.*", .features = "1 queue_if_no_path", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* IBM RSSM */ .vendor = "IBM", .product = "1820N00", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = 100, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* IBM XIV Storage System */ .vendor = "IBM", .product = "2810XIV", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = 15, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 15, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * IBM Power Virtual SCSI Devices * * Maintainer : Brian King, IBM * Mail : brking@linux.vnet.ibm.com */ { /* AIX VDASD */ .vendor = "AIX", .product = "VDASD", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM 3303 NVDISK */ .vendor = "IBM", .product = "3303 NVDISK", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = FAILOVER, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* AIX NVDISK */ .vendor = "AIX", .product = "NVDISK", .features = DEFAULT_FEATURES, .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* DELL MD3000 */ .vendor = "DELL", .product = "MD3000", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD3000i */ .vendor = "DELL", .product = "MD3000i", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD32xx */ .vendor = "DELL", .product = "MD32xx", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD32xxi */ .vendor = "DELL", .product = "MD32xxi", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD36xxi */ .vendor = "DELL", .product = "MD36xxi", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD36xxf */ .vendor = "DELL", .product = "MD36xxf", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* * NETAPP controller family * * Maintainer : Dave Wysochanski * Mail : davidw@netapp.com */ { .vendor = "NETAPP", .product = "LUN.*", .features = "3 queue_if_no_path pg_init_retries 50", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .flush_on_last_del = FLUSH_ENABLED, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, .dev_loss = MAX_DEV_LOSS_TMO, .checker_name = TUR, .prio_name = PRIO_ONTAP, .prio_args = NULL, .retain_hwhandler = RETAIN_HWHANDLER_ON, .user_friendly_names = USER_FRIENDLY_NAMES_OFF, .detect_prio = DETECT_PRIO_ON, }, /* * NEXENTA/COMSTAR controller family * * Maintainer : Yacine Kheddache * Mail : yacine@alyseo.com */ { .vendor = "NEXENTA", .product = "COMSTAR", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 30, .minio = 128, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * IBM NSeries (NETAPP) controller family * * Maintainer : Dave Wysochanski * Mail : davidw@netapp.com */ { .vendor = "IBM", .product = "Nseries.*", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, .checker_name = DIRECTIO, .prio_name = PRIO_ONTAP, .prio_args = NULL, }, /* * Pillar Data controller family * * Maintainer : Srinivasan Ramani * Mail : sramani@pillardata.com */ { .vendor = "Pillar", .product = "Axiom.*", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * SGI arrays * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "SGI", .product = "TP9[13]00", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "SGI", .product = "TP9[45]00", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "SGI", .product = "IS.*", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* NEC Storage M Series */ { .vendor = "NEC", .product = "DISK ARRAY", .features = DEFAULT_FEATURES, .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * STK arrays * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "STK", .product = "OPENstorage D280", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* * SUN arrays * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "SUN", .product = "(StorEdge 3510|T4)", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "SUN", .product = "STK6580_6780", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = TUR, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "EUROLOGC", .product = "FC2502", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .checker_name = DEFAULT_CHECKER, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Pivot3 RAIGE * * Maintainer : Bart Brooks, Pivot3 * Mail : bartb@pivot3.com */ { .vendor = "PIVOT3", .product = "RAIGE VOLUME", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 100, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "SUN", .product = "CSM200_R", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* SUN/LSI 2510, 2540, 2530, 2540 */ { .vendor = "SUN", .product = "LCSM100_[IEFS]", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* StorageTek 6180 */ { .vendor = "SUN", .product = "SUN_6180", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .minio_rq = DEFAULT_MINIO_RQ, .checker_name = RDAC, .prio_name = PRIO_RDAC, }, /* LSI/Engenio/NetApp E-Series RDAC storage * * Maintainer : Sean Stewart * Mail : sean.stewart@netapp.com */ { .vendor = "(NETAPP|LSI|ENGENIO)", .product = "INF-01-00", .bl_product = "Universal Xport", .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 30, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, .detect_prio = DETECT_PRIO_ON, .retain_hwhandler = RETAIN_HWHANDLER_ON, }, { .vendor = "STK", .product = "FLEXLINE 380", .bl_product = "Universal Xport", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "Intel", .product = "Multi-Flex", .features = DEFAULT_FEATURES, .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { .vendor = "DataCore", .product = "SANmelody", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { .vendor = "DataCore", .product = "Virtual Disk", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * EOL */ { .vendor = NULL, .product = NULL, .features = NULL, .hwhandler = NULL, .selector = NULL, .pgpolicy = 0, .pgfailback = 0, .rr_weight = 0, .no_path_retry = 0, .minio = 0, .minio_rq = 0, .checker_name = NULL, .prio_name = NULL, .prio_args = NULL, }, }; extern int setup_default_hwtable (vector hw) { int r = 0; struct hwentry * hwe = default_hw; while (hwe->vendor) { r += store_hwe(hw, hwe); hwe++; } return r; } multipath-tools-0.5.0+git1.656f8865/libmultipath/hwtable.h000066400000000000000000000001471260771327300230500ustar00rootroot00000000000000#ifndef _HWTABLE_H #define _HWTABLE_H int setup_default_hwtable (vector hw); #endif /* _HWTABLE_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/list.h000066400000000000000000000212671260771327300224030ustar00rootroot00000000000000/* * Copied from the Linux kernel source tree, version 2.6.0-test1. * * Licensed under the GPL v2 as per the whole kernel source tree. * */ #ifndef _LIST_H #define _LIST_H #include /** * container_of - cast a member of a structure out to the containing structure * * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(struct list_head *head) { return head->next == head; } static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head, head->next); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head->prev, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head, head->next); INIT_LIST_HEAD(list); } } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) #endif /* _LIST_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/lock.c000066400000000000000000000002101260771327300223340ustar00rootroot00000000000000#include #include "lock.h" #include void cleanup_lock (void * data) { unlock ((*(struct mutex_lock *)data)); } multipath-tools-0.5.0+git1.656f8865/libmultipath/lock.h000066400000000000000000000020511260771327300223460ustar00rootroot00000000000000#ifndef _LOCK_H #define _LOCK_H #include /* * Wrapper for the mutex. Includes a ref-count to keep * track of how many there are out-standing threads blocking * on a mutex. */ struct mutex_lock { pthread_mutex_t *mutex; int depth; }; #ifdef LCKDBG #define lock(a) \ fprintf(stderr, "%s:%s(%i) lock %p depth: %d (%ld)\n", __FILE__, __FUNCTION__, __LINE__, a.mutex, a.depth, pthread_self()); \ a.depth++; pthread_mutex_lock(a.mutex) #define unlock(a) \ fprintf(stderr, "%s:%s(%i) unlock %p depth: %d (%ld)\n", __FILE__, __FUNCTION__, __LINE__, a.mutex, a.depth, pthread_self()); \ a.depth--; pthread_mutex_unlock(a.mutex) #define lock_cleanup_pop(a) \ fprintf(stderr, "%s:%s(%i) unlock %p depth: %d (%ld)\n", __FILE__, __FUNCTION__, __LINE__, a.mutex, a.depth, pthread_self()); \ pthread_cleanup_pop(1); #else #define lock(a) a.depth++; pthread_mutex_lock(a.mutex) #define unlock(a) a.depth--; pthread_mutex_unlock(a.mutex) #define lock_cleanup_pop(a) pthread_cleanup_pop(1); #endif void cleanup_lock (void * data); #endif /* _LOCK_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/log.c000066400000000000000000000100111260771327300221650ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Jun'ichi Nomura, NEC */ #include #include #include #include #include #include #include "memory.h" #include "log.h" #define ALIGN(len, s) (((len)+(s)-1)/(s)*(s)) #if LOGDBG static void dump_logarea (void) { struct logmsg * msg; logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n", la->start, la->end); logdbg(stderr, "|addr |next |prio|msg\n"); for (msg = (struct logmsg *)la->head; (void *)msg != la->tail; msg = msg->next) logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); logdbg(stderr, "\n\n"); } #endif static int logarea_init (int size) { logdbg(stderr,"enter logarea_init\n"); la = (struct logarea *)MALLOC(sizeof(struct logarea)); if (!la) return 1; if (size < MAX_MSG_SIZE) size = DEFAULT_AREA_SIZE; la->start = MALLOC(size); if (!la->start) { FREE(la); return 1; } memset(la->start, 0, size); la->empty = 1; la->end = la->start + size; la->head = la->start; la->tail = la->start; la->buff = MALLOC(MAX_MSG_SIZE + sizeof(struct logmsg)); if (!la->buff) { FREE(la->start); FREE(la); return 1; } return 0; } int log_init(char *program_name, int size) { logdbg(stderr,"enter log_init\n"); openlog(program_name, 0, LOG_DAEMON); if (logarea_init(size)) return 1; return 0; } void free_logarea (void) { FREE(la->start); FREE(la->buff); FREE(la); return; } void log_close (void) { free_logarea(); closelog(); return; } void log_reset (char *program_name) { closelog(); tzset(); openlog(program_name, 0, LOG_DAEMON); } int log_enqueue (int prio, const char * fmt, va_list ap) { int len, fwd; char buff[MAX_MSG_SIZE]; struct logmsg * msg; struct logmsg * lastmsg; lastmsg = (struct logmsg *)la->tail; if (!la->empty) { fwd = sizeof(struct logmsg) + strlen((char *)&lastmsg->str) * sizeof(char) + 1; la->tail += ALIGN(fwd, sizeof(void *)); } vsnprintf(buff, MAX_MSG_SIZE, fmt, ap); len = ALIGN(sizeof(struct logmsg) + strlen(buff) * sizeof(char) + 1, sizeof(void *)); /* not enough space on tail : rewind */ if (la->head <= la->tail && len > (la->end - la->tail)) { logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); if (la->head == la->start ) { logdbg(stderr, "enqueue: can not rewind tail, drop msg\n"); la->tail = lastmsg; return 1; /* can't reuse */ } la->tail = la->start; if (la->empty) la->head = la->start; } /* not enough space on head : drop msg */ if (la->head > la->tail && len >= (la->head - la->tail)) { logdbg(stderr, "enqueue: log area overrun, drop msg\n"); if (!la->empty) la->tail = lastmsg; return 1; } /* ok, we can stage the msg in the area */ la->empty = 0; msg = (struct logmsg *)la->tail; msg->prio = prio; memcpy((void *)&msg->str, buff, strlen(buff) + 1); lastmsg->next = la->tail; msg->next = la->head; logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); #if LOGDBG dump_logarea(); #endif return 0; } int log_dequeue (void * buff) { struct logmsg * src = (struct logmsg *)la->head; struct logmsg * dst = (struct logmsg *)buff; struct logmsg * lst = (struct logmsg *)la->tail; if (la->empty) return 1; int len = strlen((char *)&src->str) * sizeof(char) + sizeof(struct logmsg) + 1; dst->prio = src->prio; memcpy(dst, src, len); if (la->tail == la->head) la->empty = 1; /* we purge the last logmsg */ else { la->head = src->next; lst->next = la->head; } logdbg(stderr, "dequeue: %p, %p, %i, %s\n", (void *)src, src->next, src->prio, (char *)&src->str); memset((void *)src, 0, len); return 0; } /* * this one can block under memory pressure */ void log_syslog (void * buff) { struct logmsg * msg = (struct logmsg *)buff; syslog(msg->prio, "%s", (char *)&msg->str); } multipath-tools-0.5.0+git1.656f8865/libmultipath/log.h000066400000000000000000000013451260771327300222040ustar00rootroot00000000000000#ifndef LOG_H #define LOG_H #define DEFAULT_AREA_SIZE 16384 #define MAX_MSG_SIZE 256 #ifndef LOGLEVEL #define LOGLEVEL 5 #endif #if LOGDBG #define logdbg(file, fmt, args...) fprintf(file, fmt, ##args) #else #define logdbg(file, fmt, args...) do {} while (0) #endif struct logmsg { short int prio; void * next; char str[0]; }; struct logarea { int empty; void * head; void * tail; void * start; void * end; char * buff; }; struct logarea * la; int log_init (char * progname, int size); void log_close (void); void log_reset (char * progname); int log_enqueue (int prio, const char * fmt, va_list ap); int log_dequeue (void *); void log_syslog (void *); void dump_logmsg (void *); void free_logarea (void); #endif /* LOG_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/log_pthread.c000066400000000000000000000047451260771327300237150ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include "log_pthread.h" #include "log.h" #include "lock.h" pthread_t log_thr; pthread_mutex_t logq_lock; pthread_mutex_t logev_lock; pthread_cond_t logev_cond; int logq_running; void log_safe (int prio, const char * fmt, va_list ap) { if (log_thr == (pthread_t)0) { vsyslog(prio, fmt, ap); return; } pthread_mutex_lock(&logq_lock); log_enqueue(prio, fmt, ap); pthread_mutex_unlock(&logq_lock); pthread_mutex_lock(&logev_lock); 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; 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 * log_thread (void * et) { int running; pthread_mutex_lock(&logev_lock); logq_running = 1; pthread_mutex_unlock(&logev_lock); mlockall(MCL_CURRENT | MCL_FUTURE); logdbg(stderr,"enter log_thread\n"); while (1) { pthread_mutex_lock(&logev_lock); pthread_cond_wait(&logev_cond, &logev_lock); running = logq_running; pthread_mutex_unlock(&logev_lock); if (!running) break; log_thread_flush(); } return NULL; } void log_thread_start (pthread_attr_t *attr) { logdbg(stderr,"enter log_thread_start\n"); pthread_mutex_init(&logq_lock, NULL); pthread_mutex_init(&logev_lock, NULL); pthread_cond_init(&logev_cond, NULL); if (log_init("multipathd", 0)) { fprintf(stderr,"can't initialize log buffer\n"); exit(1); } if (pthread_create(&log_thr, attr, log_thread, NULL)) { fprintf(stderr,"can't start log thread\n"); exit(1); } return; } void log_thread_stop (void) { logdbg(stderr,"enter log_thread_stop\n"); pthread_mutex_lock(&logev_lock); logq_running = 0; pthread_cond_signal(&logev_cond); pthread_mutex_unlock(&logev_lock); pthread_mutex_lock(&logq_lock); pthread_cancel(log_thr); pthread_mutex_unlock(&logq_lock); pthread_join(log_thr, NULL); log_thr = (pthread_t)0; flush_logqueue(); pthread_mutex_destroy(&logq_lock); pthread_mutex_destroy(&logev_lock); pthread_cond_destroy(&logev_cond); log_close(); } multipath-tools-0.5.0+git1.656f8865/libmultipath/log_pthread.h000066400000000000000000000006341260771327300237130ustar00rootroot00000000000000#ifndef _LOG_PTHREAD_H #define _LOG_PTHREAD_H #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_stop(void); void log_thread_flush(void); #endif /* _LOG_PTHREAD_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/memory.c000066400000000000000000000253551260771327300227350ustar00rootroot00000000000000/* * Part: Memory management framework. This framework is used to * find any memory leak. * * Version: $Id: memory.c,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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. * * 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. * * Copyright (C) 2001-2005 Alexandre Cassen, */ #include "memory.h" /* Global var */ unsigned long mem_allocated; /* Total memory used in Bytes */ void * xalloc(unsigned long size) { void *mem; if ((mem = malloc(size))) mem_allocated += size; return mem; } void * zalloc(unsigned long size) { void *mem; if ((mem = malloc(size))) { memset(mem, 0, size); mem_allocated += size; } return mem; } void xfree(void *p) { mem_allocated -= sizeof (p); free(p); p = NULL; } /* * Memory management. in debug mode, * help finding eventual memory leak. * Allocation memory types manipulated are : * * +type+--------meaning--------+ * ! 0 ! Free slot ! * ! 1 ! Overrun ! * ! 2 ! free null ! * ! 3 ! realloc null ! * ! 4 ! Not previus allocated ! * ! 8 ! Last free list ! * ! 9 ! Allocated ! * +----+-----------------------+ * * global variabel debug bit 9 ( 512 ) used to * flag some memory error. * */ #ifdef _DEBUG_ typedef struct { int type; int line; char *func; char *file; void *ptr; unsigned long size; long csum; } MEMCHECK; /* Last free pointers */ static MEMCHECK free_list[256]; static MEMCHECK alloc_list[MAX_ALLOC_LIST]; static int number_alloc_list = 0; static int n = 0; /* Alloc list pointer */ static int f = 0; /* Free list pointer */ void * dbg_malloc(unsigned long size, char *file, char *function, int line) { void *buf; int i = 0; long check; buf = zalloc(size + sizeof (long)); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; while (i < number_alloc_list) { if (alloc_list[i].type == 0) break; i++; } if (i == number_alloc_list) number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].csum = check; alloc_list[i].type = 9; if (debug & 1) printf("zalloc[%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, size, file, line, function); n++; return buf; } char * dbg_strdup(char *str, char *file, char *function, int line) { void *buf; int i = 0; long check; long size; size = strlen(str) + 1; buf = zalloc(size + sizeof (long)); strcat(buf, str); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; while (i < number_alloc_list) { if (alloc_list[i].type == 0) break; i++; } if (i == number_alloc_list) number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].csum = check; alloc_list[i].type = 9; if (debug & 1) printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, size, file, line, function); n++; return buf; } /* Display a buffer into a HEXA formated output */ static void dump_buffer(char *buff, int count) { int i, j, c; int printnext = 1; if (count % 16) c = count + (16 - count % 16); else c = count; for (i = 0; i < c; i++) { if (printnext) { printnext--; printf("%.4x ", i & 0xffff); } if (i < count) printf("%3.2x", buff[i] & 0xff); else printf(" "); if (!((i + 1) % 8)) { if ((i + 1) % 16) printf(" -"); else { printf(" "); for (j = i - 15; j <= i; j++) if (j < count) { if ((buff[j] & 0xff) >= 0x20 && (buff[j] & 0xff) <= 0x7e) printf("%c", buff[j] & 0xff); else printf("."); } else printf(" "); printf("\n"); printnext = 1; } } } } int dbg_free(void *buffer, char *file, char *function, int line) { int i = 0; void *buf; /* If nullpointer remember */ if (buffer == NULL) { i = number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buffer; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 2; if (debug & 1) printf("free NULL in %s, %3d, %s\n", file, line, function); debug |= 512; /* Memory Error detect */ return n; } else buf = buffer; while (i < number_alloc_list) { if (alloc_list[i].type == 9 && alloc_list[i].ptr == buf) { if (* ((long *) ((char *) alloc_list[i].ptr + alloc_list[i].size)) == alloc_list[i].csum) alloc_list[i].type = 0; /* Release */ else { alloc_list[i].type = 1; /* Overrun */ if (debug & 1) { printf("free corrupt, buffer overrun [%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, alloc_list[i].size, file, line, function); dump_buffer(alloc_list[i].ptr, alloc_list[i].size + sizeof (long)); printf("Check_sum\n"); dump_buffer((char *) &alloc_list[i].csum, sizeof(long)); debug |= 512; /* Memory Error detect */ } } break; } i++; } /* Not found */ if (i == number_alloc_list) { printf("Free ERROR %p\n", buffer); number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 4; debug |= 512; return n; } if (buffer != NULL) xfree(buffer); if (debug & 1) printf("free [%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, alloc_list[i].size, file, line, function); free_list[f].file = file; free_list[f].line = line; free_list[f].func = function; free_list[f].ptr = buffer; free_list[f].type = 8; free_list[f].csum = i; /* Using this field for row id */ f++; f &= 255; n--; return n; } void dbg_free_final(char *banner) { unsigned int sum = 0, overrun = 0, badptr = 0; int i, j; i = 0; printf("\n---[ Memory dump for (%s)]---\n\n", banner); while (i < number_alloc_list) { switch (alloc_list[i].type) { case 3: badptr++; printf ("null pointer to realloc(nil,%ld)! at %s, %3d, %s\n", alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 4: badptr++; printf ("pointer not found in table to free(%p) [%3d:%3d], at %s, %3d, %s\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); for (j = 0; j < 256; j++) if (free_list[j].ptr == alloc_list[i].ptr) if (free_list[j].type == 8) printf (" -> pointer already released at [%3d:%3d], at %s, %3d, %s\n", (int) free_list[j].csum, number_alloc_list, free_list[j].file, free_list[j].line, free_list[j].func); break; case 2: badptr++; printf("null pointer to free(nil)! at %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 1: overrun++; printf("%p [%3d:%3d], %4ld buffer overrun!:\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].size); printf(" --> source of malloc: %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 9: sum += alloc_list[i].size; printf("%p [%3d:%3d], %4ld not released!:\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].size); printf(" --> source of malloc: %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; } i++; } printf("\n\n---[ Memory dump summary for (%s) ]---\n", banner); printf("Total number of bytes not freed...: %d\n", sum); printf("Number of entries not freed.......: %d\n", n); printf("Maximum allocated entries.........: %d\n", number_alloc_list); printf("Number of bad entries.............: %d\n", badptr); printf("Number of buffer overrun..........: %d\n\n", overrun); if (sum || n || badptr || overrun) printf("=> Program seems to have some memory problem !!!\n\n"); else printf("=> Program seems to be memory allocation safe...\n\n"); } void * dbg_realloc(void *buffer, unsigned long size, char *file, char *function, int line) { int i = 0; void *buf, *buf2; long check; if (buffer == NULL) { printf("realloc %p %s, %3d %s\n", buffer, file, line, function); i = number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = NULL; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 3; return dbg_malloc(size, file, function, line); } buf = buffer; while (i < number_alloc_list) { if (alloc_list[i].ptr == buf) { buf = alloc_list[i].ptr; break; } i++; } /* not found */ if (i == number_alloc_list) { printf("realloc ERROR no matching zalloc %p \n", buffer); number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 9; debug |= 512; /* Memory Error detect */ return NULL; } buf2 = ((char *) buf) + alloc_list[i].size; if (*(long *) (buf2) != alloc_list[i].csum) { alloc_list[i].type = 1; debug |= 512; /* Memory Error detect */ } buf = realloc(buffer, size + sizeof (long)); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; alloc_list[i].csum = check; if (debug & 1) printf("realloc [%3d:%3d] %p, %4ld %s %d %s -> %p %4ld %s %d %s\n", i, number_alloc_list, alloc_list[i].ptr, alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func, buf, size, file, line, function); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].line = line; alloc_list[i].func = function; return buf; } #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/memory.h000066400000000000000000000044571260771327300227420ustar00rootroot00000000000000/* * Part: memory.c include file. * * Version: $Id: memory.h,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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. * * 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. * * Copyright (C) 2001-2005 Alexandre Cassen, */ #ifndef _MEMORY_H #define _MEMORY_H /* system includes */ #include #include #include #include #include /* extern types */ extern unsigned long mem_allocated; extern void *xalloc(unsigned long size); extern void *zalloc(unsigned long size); extern void xfree(void *p); /* Global alloc macro */ #define ALLOC(n) (xalloc(n)) /* Local defines */ #ifdef _DEBUG_ int debug; #define MAX_ALLOC_LIST 2048 #define MALLOC(n) ( dbg_malloc((n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define FREE(b) ( dbg_free((b), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define REALLOC(b,n) ( dbg_realloc((b), (n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define STRDUP(n) ( dbg_strdup((n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) /* Memory debug prototypes defs */ extern void *dbg_malloc(unsigned long, char *, char *, int); extern int dbg_free(void *, char *, char *, int); extern void *dbg_realloc(void *, unsigned long, char *, char *, int); extern char *dbg_strdup(char *, char *, char *, int); extern void dbg_free_final(char *); #else #define MALLOC(n) (zalloc(n)) #define FREE(p) (xfree(p)) #define REALLOC(p,n) (realloc((p),(n))) #define STRDUP(n) (strdup(n)) #endif /* Common defines */ #define FREE_PTR(P) if((P)) FREE((P)); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/parser.c000066400000000000000000000272461260771327300227220ustar00rootroot00000000000000/* * Part: Configuration file parser/reader. Place into the dynamic * data structure representation the conf file * * Version: $Id: parser.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * 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. */ #include #include #include "parser.h" #include "memory.h" #include "debug.h" /* local vars */ static int sublevel = 0; static vector keywords = NULL; static vector *keywords_addr = NULL; static int line_nr; void set_current_keywords (vector *k) { keywords_addr = k; keywords = NULL; } int keyword_alloc(vector keywords, char *string, int (*handler) (vector), int (*print) (char *, int, void *), int unique) { struct keyword *keyword; keyword = (struct keyword *) MALLOC(sizeof (struct keyword)); if (!keyword) return 1; if (!vector_alloc_slot(keywords)) { FREE(keyword); return 1; } keyword->string = string; keyword->handler = handler; keyword->print = print; keyword->unique = unique; vector_set_slot(keywords, keyword); return 0; } int install_keyword_root(char *string, int (*handler) (vector)) { int r = keyword_alloc(keywords, string, handler, NULL, 1); if (!r) *keywords_addr = keywords; return r; } void install_sublevel(void) { sublevel++; } void install_sublevel_end(void) { sublevel--; } int _install_keyword(char *string, int (*handler) (vector), int (*print) (char *, int, void *), int unique) { int i = 0; struct keyword *keyword; /* fetch last keyword */ keyword = VECTOR_SLOT(keywords, VECTOR_SIZE(keywords) - 1); /* position to last sub level */ for (i = 0; i < sublevel; i++) keyword = VECTOR_SLOT(keyword->sub, VECTOR_SIZE(keyword->sub) - 1); /* First sub level allocation */ if (!keyword->sub) keyword->sub = vector_alloc(); if (!keyword->sub) return 1; /* add new sub keyword */ return keyword_alloc(keyword->sub, string, handler, print, unique); } void free_keywords(vector keywords) { struct keyword *keyword; int i; if (!keywords) return; for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (keyword->sub) free_keywords(keyword->sub); FREE(keyword); } vector_free(keywords); } struct keyword * find_keyword(vector v, char * name) { struct keyword *keyword; int i; int len; if (!name || !keywords) return NULL; if (!v) v = keywords; len = strlen(name); for (i = 0; i < VECTOR_SIZE(v); i++) { keyword = VECTOR_SLOT(v, i); if ((strlen(keyword->string) == len) && !strcmp(keyword->string, name)) return keyword; if (keyword->sub) { keyword = find_keyword(keyword->sub, name); if (keyword) return keyword; } } return NULL; } int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data) { int r; int fwd = 0; char *f = fmt; if (!kw || !kw->print) return 0; do { if (fwd == len || *f == '\0') break; if (*f != '%') { *(buff + fwd) = *f; fwd++; continue; } f++; switch(*f) { case 'k': fwd += snprintf(buff + fwd, len - fwd, "%s", kw->string); break; case 'v': r = kw->print(buff + fwd, len - fwd, data); if (!r) { /* no output if no value */ buff = '\0'; return 0; } fwd += r; break; } if (fwd > len) fwd = len; } while (*f++); return fwd; } vector alloc_strvec(char *string) { char *cp, *start, *token; int strlen; int in_string; vector strvec; if (!string) return NULL; cp = string; /* Skip white spaces */ while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0') cp++; /* Return if there is only white spaces */ if (*cp == '\0') return NULL; /* Return if string begin with a comment */ if (*cp == '!' || *cp == '#') return NULL; /* Create a vector and alloc each command piece */ strvec = vector_alloc(); if (!strvec) return NULL; in_string = 0; while (1) { if (!vector_alloc_slot(strvec)) goto out; start = cp; if (*cp == '"') { cp++; token = MALLOC(2); if (!token) goto out; *(token) = '"'; *(token + 1) = '\0'; if (in_string) in_string = 0; else in_string = 1; } else if (!in_string && (*cp == '{' || *cp == '}')) { token = MALLOC(2); if (!token) goto out; *(token) = *cp; *(token + 1) = '\0'; cp++; } else { while ((in_string || (!isspace((int) *cp) && isascii((int) *cp) && *cp != '!' && *cp != '#' && *cp != '{' && *cp != '}')) && *cp != '\0' && *cp != '"') cp++; strlen = cp - start; token = MALLOC(strlen + 1); if (!token) goto out; memcpy(token, start, strlen); *(token + strlen) = '\0'; } vector_set_slot(strvec, token); while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0') cp++; if (*cp == '\0' || *cp == '!' || *cp == '#') return strvec; } out: vector_free(strvec); return NULL; } static int read_line(FILE *stream, char *buf, int size) { char *p; if (fgets(buf, size, stream) == NULL) return 0; strtok_r(buf, "\n\r", &p); return 1; } void * set_value(vector strvec) { char *str = VECTOR_SLOT(strvec, 1); size_t size; int i = 0; int len = 0; char *alloc = NULL; char *tmp; if (!str) { condlog(0, "option '%s' missing value", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } size = strlen(str); if (size == 0) { condlog(0, "option '%s' has empty value", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*str != '"') { alloc = MALLOC(sizeof (char) * (size + 1)); if (alloc) memcpy(alloc, str, size); else condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return alloc; } /* Even empty quotes counts as a value (An empty string) */ alloc = (char *) MALLOC(sizeof (char)); if (!alloc) { condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (!str) { free(alloc); condlog(0, "parse error for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*str == '"') break; tmp = alloc; /* The first +1 is for the NULL byte. The rest are for the * spaces between words */ len += strlen(str) + 1; alloc = REALLOC(alloc, sizeof (char) * len); if (!alloc) { FREE(tmp); condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*alloc != '\0') strncat(alloc, " ", 1); strncat(alloc, str, strlen(str)); } return alloc; } /* non-recursive configuration stream handler */ static int kw_level = 0; int warn_on_duplicates(vector uniques, char *str, char *file) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) { if (!strcmp(str, tmp)) { condlog(1, "%s line %d, duplicate keyword: %s", file, line_nr, str); return 0; } } tmp = strdup(str); if (!tmp) return 1; if (!vector_alloc_slot(uniques)) { free(tmp); return 1; } vector_set_slot(uniques, tmp); return 0; } void free_uniques(vector uniques) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) free(tmp); vector_free(uniques); } int is_sublevel_keyword(char *str) { return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 || strcmp(str, "blacklist_exceptions") == 0 || strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 || strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 || strcmp(str, "multipath") == 0); } int validate_config_strvec(vector strvec, char *file) { char *str; int i; str = VECTOR_SLOT(strvec, 0); if (str == NULL) { condlog(0, "can't parse option on line %d of %s", line_nr, file); return -1; } if (*str == '}') { if (VECTOR_SIZE(strvec) > 1) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file); return 0; } if (*str == '{') { condlog(0, "invalid keyword '%s' on line %d of %s", str, line_nr, file); return -1; } if (is_sublevel_keyword(str)) { str = VECTOR_SLOT(strvec, 1); if (str == NULL) condlog(0, "missing '{' on line %d of %s", line_nr, file); else if (*str != '{') condlog(0, "expecting '{' on line %d of %s. found '%s'", line_nr, file, str); else if (VECTOR_SIZE(strvec) > 2) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } str = VECTOR_SLOT(strvec, 1); if (str == NULL) { condlog(0, "missing value for option '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 0), line_nr, file); return -1; } if (*str != '"') { if (VECTOR_SIZE(strvec) > 2) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (str == NULL) { condlog(0, "can't parse value on line %d of %s", line_nr, file); return -1; } if (*str == '"') { if (VECTOR_SIZE(strvec) > i + 1) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file); return 0; } } condlog(0, "missing closing quotes on line %d of %s", line_nr, file); return 0; } static int process_stream(FILE *stream, vector keywords, char *file) { int i; int r = 0, t; struct keyword *keyword; char *str; char *buf; vector strvec; vector uniques; uniques = vector_alloc(); if (!uniques) return 1; buf = MALLOC(MAXBUF); if (!buf) { vector_free(uniques); return 1; } while (read_line(stream, buf, MAXBUF)) { line_nr++; strvec = alloc_strvec(buf); if (!strvec) continue; if (validate_config_strvec(strvec, file) != 0) { free_strvec(strvec); continue; } str = VECTOR_SLOT(strvec, 0); if (!strcmp(str, EOB)) { if (kw_level > 0) { free_strvec(strvec); break; } condlog(0, "unmatched '%s' at line %d of %s", EOB, line_nr, file); } for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (!strcmp(keyword->string, str)) { if (keyword->unique && warn_on_duplicates(uniques, str, file)) { r = 1; free_strvec(strvec); goto out; } if (keyword->handler) { t = (*keyword->handler) (strvec); r += t; if (t) condlog(1, "multipath.conf +%d, parsing failed: %s", line_nr, buf); } if (keyword->sub) { kw_level++; r += process_stream(stream, keyword->sub, file); kw_level--; } break; } } if (i >= VECTOR_SIZE(keywords)) condlog(1, "%s line %d, invalid keyword: %s", file, line_nr, str); free_strvec(strvec); } out: FREE(buf); free_uniques(uniques); return r; } int alloc_keywords(void) { if (!keywords) keywords = vector_alloc(); if (!keywords) return 1; return 0; } /* Data initialization */ int process_file(char *file) { int r; FILE *stream; if (!keywords) { condlog(0, "No keywords alocated"); return 1; } stream = fopen(file, "r"); if (!stream) { condlog(0, "couldn't open configuration file '%s': %s", file, strerror(errno)); return 1; } /* Stream handling */ line_nr = 0; r = process_stream(stream, keywords, file); fclose(stream); //free_keywords(keywords); return r; } multipath-tools-0.5.0+git1.656f8865/libmultipath/parser.h000066400000000000000000000052461260771327300227230ustar00rootroot00000000000000/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: cfreader.c include file. * * Version: $Id: parser.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * 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. */ #ifndef _PARSER_H #define _PARSER_H /* system includes */ #include #include #include #include #include #include /* local includes */ #include "vector.h" /* Global definitions */ #define EOB "}" #define MAXBUF 1024 /* ketword definition */ struct keyword { char *string; int (*handler) (vector); int (*print) (char *, int, void *); vector sub; int unique; }; /* Reloading helpers */ #define SET_RELOAD (reload = 1) #define UNSET_RELOAD (reload = 0) #define RELOAD_DELAY 5 /* iterator helper */ #define iterate_sub_keywords(k,p,i) \ for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++) /* Prototypes */ extern int keyword_alloc(vector keywords, char *string, int (*handler) (vector), int (*print) (char *, int, void *), int unique); extern int install_keyword_root(char *string, int (*handler) (vector)); extern void install_sublevel(void); extern void install_sublevel_end(void); extern int _install_keyword(char *string, int (*handler) (vector), int (*print) (char *, int, void *), int unique); #define install_keyword(str, vec, pri) _install_keyword(str, vec, pri, 1) #define install_keyword_multi(str, vec, pri) _install_keyword(str, vec, pri, 0) extern void dump_keywords(vector keydump, int level); extern void free_keywords(vector keywords); extern vector alloc_strvec(char *string); extern void *set_value(vector strvec); extern int alloc_keywords(void); extern int process_file(char *conf_file); extern struct keyword * find_keyword(vector v, char * name); void set_current_keywords (vector *k); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/pgpolicies.c000066400000000000000000000146001260771327300235520ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include "checkers.h" #include "util.h" #include "memory.h" #include "vector.h" #include "structs.h" #include "pgpolicies.h" #include "switchgroup.h" extern int get_pgpolicy_id (char * str) { if (0 == strncmp(str, "failover", 8)) return FAILOVER; if (0 == strncmp(str, "multibus", 8)) return MULTIBUS; if (0 == strncmp(str, "group_by_serial", 15)) return GROUP_BY_SERIAL; if (0 == strncmp(str, "group_by_prio", 13)) return GROUP_BY_PRIO; if (0 == strncmp(str, "group_by_node_name", 18)) return GROUP_BY_NODE_NAME; return IOPOLICY_UNDEF; } extern int get_pgpolicy_name (char * buff, int len, int id) { char * s; switch (id) { case FAILOVER: s = "failover"; break; case MULTIBUS: s = "multibus"; break; case GROUP_BY_SERIAL: s = "group_by_serial"; break; case GROUP_BY_PRIO: s = "group_by_prio"; break; case GROUP_BY_NODE_NAME: s = "group_by_node_name"; break; default: s = "undefined"; break; } return snprintf(buff, POLICY_NAME_SIZE, "%s", s); } void sort_pathgroups (struct multipath *mp) { int i, j; struct pathgroup * pgp1, * pgp2; if (!mp->pg) return; vector_foreach_slot(mp->pg, pgp1, i) { path_group_prio_update(pgp1); for (j = i - 1; j >= 0; j--) { pgp2 = VECTOR_SLOT(mp->pg, j); if (!pgp2) continue; if (pgp2->priority > pgp1->priority || (pgp2->priority == pgp1->priority && pgp2->enabled_paths >= pgp1->enabled_paths)) { vector_move_up(mp->pg, i, j + 1); break; } } if (j < 0 && i != 0) vector_move_up(mp->pg, i, 0); } } /* * One path group per unique tgt_node_name present in the path vector */ extern int group_by_node_name (struct multipath * mp) { int i, j; int * bitmap; struct path * pp; struct pathgroup * pgp; struct path * pp2; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; /* init the bitmap */ bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); if (!bitmap) goto out; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { if (bitmap[i]) continue; pp = VECTOR_SLOT(mp->paths, i); /* here, we really got a new pg */ pgp = alloc_pathgroup(); if (!pgp) goto out1; if (store_pathgroup(mp->pg, pgp)) goto out1; /* feed the first path */ if (store_path(pgp->paths, pp)) goto out1; bitmap[i] = 1; for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { if (bitmap[j]) continue; pp2 = VECTOR_SLOT(mp->paths, j); if (!strncmp(pp->tgt_node_name, pp2->tgt_node_name, NODE_NAME_SIZE)) { if (store_path(pgp->paths, pp2)) goto out1; bitmap[j] = 1; } } } FREE(bitmap); sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out1: FREE(bitmap); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } /* * One path group per unique serial number present in the path vector */ extern int group_by_serial (struct multipath * mp) { int i, j; int * bitmap; struct path * pp; struct pathgroup * pgp; struct path * pp2; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; /* init the bitmap */ bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); if (!bitmap) goto out; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { if (bitmap[i]) continue; pp = VECTOR_SLOT(mp->paths, i); /* here, we really got a new pg */ pgp = alloc_pathgroup(); if (!pgp) goto out1; if (store_pathgroup(mp->pg, pgp)) goto out1; /* feed the first path */ if (store_path(pgp->paths, pp)) goto out1; bitmap[i] = 1; for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { if (bitmap[j]) continue; pp2 = VECTOR_SLOT(mp->paths, j); if (0 == strcmp(pp->serial, pp2->serial)) { if (store_path(pgp->paths, pp2)) goto out1; bitmap[j] = 1; } } } FREE(bitmap); sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out1: FREE(bitmap); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } extern int one_path_per_group (struct multipath * mp) { int i; struct path * pp; struct pathgroup * pgp; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { pp = VECTOR_SLOT(mp->paths, i); pgp = alloc_pathgroup(); if (!pgp) goto out; if (store_pathgroup(mp->pg, pgp)) goto out; if (store_path(pgp->paths, pp)) goto out; } sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } extern int one_group (struct multipath * mp) /* aka multibus */ { struct pathgroup * pgp; if (VECTOR_SIZE(mp->paths) < 0) return 0; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; if (VECTOR_SIZE(mp->paths) > 0) { pgp = alloc_pathgroup(); if (!pgp) goto out; vector_free(pgp->paths); pgp->paths = mp->paths; mp->paths = NULL; if (store_pathgroup(mp->pg, pgp)) goto out; } return 0; out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } extern int group_by_prio (struct multipath * mp) { int i; unsigned int prio; struct path * pp; struct pathgroup * pgp; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; while (VECTOR_SIZE(mp->paths) > 0) { pp = VECTOR_SLOT(mp->paths, 0); prio = pp->priority; /* * Find the position to insert the new path group. All groups * are ordered by the priority value (higher value first). */ vector_foreach_slot(mp->pg, pgp, i) { pp = VECTOR_SLOT(pgp->paths, 0); if (prio > pp->priority) break; } /* * Initialize the new path group. */ pgp = alloc_pathgroup(); if (!pgp) goto out; if (store_path(pgp->paths, VECTOR_SLOT(mp->paths, 0))) goto out; vector_del_slot(mp->paths, 0); /* * Store the new path group into the vector. */ if (i < VECTOR_SIZE(mp->pg)) { if (!vector_insert_slot(mp->pg, i, pgp)) goto out; } else { if (store_pathgroup(mp->pg, pgp)) goto out; } /* * add the other paths with the same prio */ vector_foreach_slot(mp->paths, pp, i) { if (pp->priority == prio) { if (store_path(pgp->paths, pp)) goto out; vector_del_slot(mp->paths, i); i--; } } } free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } multipath-tools-0.5.0+git1.656f8865/libmultipath/pgpolicies.h000066400000000000000000000011121260771327300235510ustar00rootroot00000000000000#ifndef _PGPOLICIES_H #define _PGPOLICIES_H #if 0 #ifndef _MAIN_H #include "main.h" #endif #endif #define POLICY_NAME_SIZE 32 /* Storage controllers capabilities */ enum iopolicies { IOPOLICY_UNDEF, FAILOVER, MULTIBUS, GROUP_BY_SERIAL, GROUP_BY_PRIO, GROUP_BY_NODE_NAME }; int get_pgpolicy_id(char *); int get_pgpolicy_name (char *, int, int); /* * policies */ int one_path_per_group(struct multipath *); int one_group(struct multipath *); int group_by_serial(struct multipath *); int group_by_prio(struct multipath *); int group_by_node_name(struct multipath *); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/print.c000066400000000000000000001060511260771327300225520ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" #include "print.h" #include "dmparser.h" #include "config.h" #include "configure.h" #include "pgpolicies.h" #include "defaults.h" #include "parser.h" #include "blacklist.h" #include "switchgroup.h" #include "devmapper.h" #include "uevent.h" #include "debug.h" #define MAX(x,y) (x > y) ? x : y #define TAIL (line + len - 1 - c) #define NOPAD s = c #define PAD(x) while ((int)(c - s) < (x) && (c < (line + len - 1))) \ *c++ = ' '; s = c #define ENDLINE \ if (c > line) \ line[c - line - 1] = '\n' #define PRINT(var, size, format, args...) \ fwd = snprintf(var, size, format, ##args); \ c += (fwd >= size) ? size : fwd; /* * information printing helpers */ static int snprint_str (char * buff, size_t len, const char * str) { return snprintf(buff, len, "%s", str); } static int snprint_int (char * buff, size_t len, int val) { return snprintf(buff, len, "%i", val); } static int snprint_uint (char * buff, size_t len, unsigned int val) { return snprintf(buff, len, "%u", val); } static int snprint_size (char * buff, size_t len, unsigned long long size) { float s = (float)(size >> 1); /* start with KB */ char fmt[6] = {}; char units[] = {'K','M','G','T','P'}; char *u = units; while (s >= 1024 && *u != 'P') { s = s / 1024; u++; } if (s < 10) snprintf(fmt, 6, "%%.1f%c", *u); else snprintf(fmt, 6, "%%.0f%c", *u); return snprintf(buff, len, fmt, s); } /* * multipath info printing functions */ static int snprint_name (char * buff, size_t len, struct multipath * mpp) { if (mpp->alias) return snprintf(buff, len, "%s", mpp->alias); else return snprintf(buff, len, "%s", mpp->wwid); } static int snprint_sysfs (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi) return snprintf(buff, len, "dm-%i", mpp->dmi->minor); else return snprintf(buff, len, "undef"); } static int snprint_ro (char * buff, size_t len, struct multipath * mpp) { if (!mpp->dmi) return snprintf(buff, len, "undef"); if (mpp->dmi->read_only) return snprintf(buff, len, "ro"); else return snprintf(buff, len, "rw"); } static int snprint_progress (char * buff, size_t len, int cur, int total) { char * c = buff; char * end = buff + len; if (total > 0) { int i = PROGRESS_LEN * cur / total; int j = PROGRESS_LEN - i; while (i-- > 0) { c += snprintf(c, len, "X"); if ((len = (end - c)) <= 1) goto out; } while (j-- > 0) { c += snprintf(c, len, "."); if ((len = (end - c)) <= 1) goto out; } } c += snprintf(c, len, " %i/%i", cur, total); out: buff[c - buff + 1] = '\0'; return (c - buff + 1); } static int snprint_failback (char * buff, size_t len, struct multipath * mpp) { if (mpp->pgfailback == -FAILBACK_IMMEDIATE) return snprintf(buff, len, "immediate"); if (mpp->pgfailback == -FAILBACK_FOLLOWOVER) return snprintf(buff, len, "followover"); if (!mpp->failback_tick) return snprintf(buff, len, "-"); else return snprint_progress(buff, len, mpp->failback_tick, mpp->pgfailback); } static int snprint_queueing (char * buff, size_t len, struct multipath * mpp) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) return snprintf(buff, len, "off"); else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) return snprintf(buff, len, "on"); else if (mpp->no_path_retry == NO_PATH_RETRY_UNDEF) return snprintf(buff, len, "-"); else if (mpp->no_path_retry > 0) { if (mpp->retry_tick) return snprintf(buff, len, "%i sec", mpp->retry_tick); else return snprintf(buff, len, "%i chk", mpp->no_path_retry); } return 0; } static int snprint_nb_paths (char * buff, size_t len, struct multipath * mpp) { return snprint_int(buff, len, mpp->nr_active); } static int snprint_dm_map_state (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi && mpp->dmi->suspended) return snprintf(buff, len, "suspend"); else return snprintf(buff, len, "active"); } static int snprint_multipath_size (char * buff, size_t len, struct multipath * mpp) { return snprint_size(buff, len, mpp->size); } static int snprint_features (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->features); } static int snprint_hwhandler (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->hwhandler); } static int snprint_path_faults (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_path_failures); } static int snprint_switch_grp (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_switchgroup); } static int snprint_map_loads (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_map_loads); } static int snprint_total_q_time (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_total_queueing_time); } static int snprint_q_timeouts (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_queueing_timeouts); } static int snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->wwid); } static int snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; int i, j; vector_foreach_slot(mpp->pg, pgp, i) { if (!pgp) continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->vendor_id) && strlen(pp->product_id)) return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } } return snprintf(buff, len, "##,##"); } static int snprint_action (char * buff, size_t len, struct multipath * mpp) { switch (mpp->action) { case ACT_REJECT: return snprint_str(buff, len, ACT_REJECT_STR); case ACT_RENAME: return snprint_str(buff, len, ACT_RENAME_STR); case ACT_RELOAD: return snprint_str(buff, len, ACT_RELOAD_STR); case ACT_CREATE: return snprint_str(buff, len, ACT_CREATE_STR); case ACT_SWITCHPG: return snprint_str(buff, len, ACT_SWITCHPG_STR); default: return 0; } } /* * path info printing functions */ static int snprint_path_uuid (char * buff, size_t len, struct path * pp) { return snprint_str(buff, len, pp->wwid); } static int snprint_hcil (char * buff, size_t len, struct path * pp) { if (!pp || pp->sg_id.host_no < 0) return snprintf(buff, len, "#:#:#:#"); return snprintf(buff, len, "%i:%i:%i:%i", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } static int snprint_dev (char * buff, size_t len, struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "-"); else return snprint_str(buff, len, pp->dev); } static int snprint_dev_t (char * buff, size_t len, struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "#:#"); else return snprint_str(buff, len, pp->dev_t); } static int snprint_offline (char * buff, size_t len, struct path * pp) { if (!pp) return snprintf(buff, len, "unknown"); else if (pp->offline) return snprintf(buff, len, "offline"); else return snprintf(buff, len, "running"); } static int snprint_chk_state (char * buff, size_t len, struct path * pp) { if (!pp) return snprintf(buff, len, "undef"); switch (pp->state) { case PATH_UP: return snprintf(buff, len, "ready"); case PATH_DOWN: return snprintf(buff, len, "faulty"); case PATH_SHAKY: return snprintf(buff, len, "shaky"); case PATH_GHOST: return snprintf(buff, len, "ghost"); case PATH_PENDING: return snprintf(buff, len, "i/o pending"); case PATH_TIMEOUT: return snprintf(buff, len, "i/o timeout"); case PATH_DELAYED: return snprintf(buff, len, "delayed"); default: return snprintf(buff, len, "undef"); } } static int snprint_dm_path_state (char * buff, size_t len, struct path * pp) { if (!pp) return snprintf(buff, len, "undef"); switch (pp->dmstate) { case PSTATE_ACTIVE: return snprintf(buff, len, "active"); case PSTATE_FAILED: return snprintf(buff, len, "failed"); default: return snprintf(buff, len, "undef"); } } static int snprint_vpr (char * buff, size_t len, struct path * pp) { return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } static int snprint_next_check (char * buff, size_t len, struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "orphan"); return snprint_progress(buff, len, pp->tick, pp->checkint); } static int snprint_pri (char * buff, size_t len, struct path * pp) { return snprint_int(buff, len, pp ? pp->priority : -1); } static int snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp) { return snprint_str(buff, len, pgp->selector); } static int snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp) { /* * path group priority is not updated for every path prio change, * but only on switch group code path. * * Printing is another reason to update. */ path_group_prio_update(pgp); return snprint_int(buff, len, pgp->priority); } static int snprint_pg_state (char * buff, size_t len, struct pathgroup * pgp) { switch (pgp->status) { case PGSTATE_ENABLED: return snprintf(buff, len, "enabled"); case PGSTATE_DISABLED: return snprintf(buff, len, "disabled"); case PGSTATE_ACTIVE: return snprintf(buff, len, "active"); default: return snprintf(buff, len, "undef"); } } static int snprint_path_size (char * buff, size_t len, struct path * pp) { return snprint_size(buff, len, pp->size); } static int snprint_path_serial (char * buff, size_t len, struct path * pp) { return snprint_str(buff, len, pp->serial); } static int snprint_path_mpp (char * buff, size_t len, struct path * pp) { if (!pp->mpp) return snprintf(buff, len, "[orphan]"); if (!pp->mpp->alias) return snprintf(buff, len, "[unknown]"); return snprint_str(buff, len, pp->mpp->alias); } static int snprint_host_attr (char * buff, size_t len, struct path * pp, char *attr) { struct udev_device *host_dev = NULL; char host_id[32]; const char *value = NULL; int ret; if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) return snprintf(buff, len, "[undef]"); sprintf(host_id, "host%d", pp->sg_id.host_no); host_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_host", host_id); if (!host_dev) { condlog(1, "%s: No fc_host device for '%s'", pp->dev, host_id); goto out; } value = udev_device_get_sysattr_value(host_dev, attr); if (value) ret = snprint_str(buff, len, value); udev_device_unref(host_dev); out: if (!value) ret = snprintf(buff, len, "[unknown]"); return ret; } static int snprint_host_wwnn (char * buff, size_t len, struct path * pp) { return snprint_host_attr(buff, len, pp, "node_name"); } static int snprint_host_wwpn (char * buff, size_t len, struct path * pp) { return snprint_host_attr(buff, len, pp, "port_name"); } static int snprint_tgt_wwpn (char * buff, size_t len, struct path * pp) { struct udev_device *rport_dev = NULL; char rport_id[32]; const char *value = NULL; int ret; if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) return snprintf(buff, len, "[undef]"); sprintf(rport_id, "rport-%d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); rport_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_remote_ports", rport_id); if (!rport_dev) { condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev, rport_id); goto out; } value = udev_device_get_sysattr_value(rport_dev, "port_name"); if (value) ret = snprint_str(buff, len, value); udev_device_unref(rport_dev); out: if (!value) ret = snprintf(buff, len, "[unknown]"); return ret; } static int snprint_tgt_wwnn (char * buff, size_t len, struct path * pp) { if (pp->tgt_node_name[0] == '\0') return snprintf(buff, len, "[undef]"); return snprint_str(buff, len, pp->tgt_node_name); } static int snprint_host_adapter (char * buff, size_t len, struct path * pp) { char adapter[SLOT_NAME_SIZE]; if (sysfs_get_host_adapter_name(pp, adapter)) return snprintf(buff, len, "[undef]"); return snprint_str(buff, len, adapter); } static int snprint_path_checker (char * buff, size_t len, struct path * pp) { struct checker * c = &pp->checker; return snprint_str(buff, len, c->name); } struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, {'d', "sysfs", 0, snprint_sysfs}, {'F', "failback", 0, snprint_failback}, {'Q', "queueing", 0, snprint_queueing}, {'N', "paths", 0, snprint_nb_paths}, {'r', "write_prot", 0, snprint_ro}, {'t', "dm-st", 0, snprint_dm_map_state}, {'S', "size", 0, snprint_multipath_size}, {'f', "features", 0, snprint_features}, {'h', "hwhandler", 0, snprint_hwhandler}, {'A', "action", 0, snprint_action}, {'0', "path_faults", 0, snprint_path_faults}, {'1', "switch_grp", 0, snprint_switch_grp}, {'2', "map_loads", 0, snprint_map_loads}, {'3', "total_q_time", 0, snprint_total_q_time}, {'4', "q_timeouts", 0, snprint_q_timeouts}, {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, {0, NULL, 0 , NULL} }; struct path_data pd[] = { {'w', "uuid", 0, snprint_path_uuid}, {'i', "hcil", 0, snprint_hcil}, {'d', "dev", 0, snprint_dev}, {'D', "dev_t", 0, snprint_dev_t}, {'t', "dm_st", 0, snprint_dm_path_state}, {'o', "dev_st", 0, snprint_offline}, {'T', "chk_st", 0, snprint_chk_state}, {'s', "vend/prod/rev", 0, snprint_vpr}, {'c', "checker", 0, snprint_path_checker}, {'C', "next_check", 0, snprint_next_check}, {'p', "pri", 0, snprint_pri}, {'S', "size", 0, snprint_path_size}, {'z', "serial", 0, snprint_path_serial}, {'m', "multipath", 0, snprint_path_mpp}, {'N', "host WWNN", 0, snprint_host_wwnn}, {'n', "target WWNN", 0, snprint_tgt_wwnn}, {'R', "host WWPN", 0, snprint_host_wwpn}, {'r', "target WWPN", 0, snprint_tgt_wwpn}, {'a', "host adapter", 0, snprint_host_adapter}, {0, NULL, 0 , NULL} }; struct pathgroup_data pgd[] = { {'s', "selector", 0, snprint_pg_selector}, {'p', "pri", 0, snprint_pg_pri}, {'t', "dm_st", 0, snprint_pg_state}, {0, NULL, 0 , NULL} }; int snprint_wildcards (char * buff, int len) { int i, fwd = 0; fwd += snprintf(buff + fwd, len - fwd, "multipath format wildcards:\n"); for (i = 0; mpd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", mpd[i].wildcard, mpd[i].header); fwd += snprintf(buff + fwd, len - fwd, "\npath format wildcards:\n"); for (i = 0; pd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", pd[i].wildcard, pd[i].header); fwd += snprintf(buff + fwd, len - fwd, "\npathgroup format wildcards:\n"); for (i = 0; pgd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", pgd[i].wildcard, pgd[i].header); return fwd; } void get_path_layout (vector pathvec, int header) { int i, j; char buff[MAX_FIELD_LEN]; struct path * pp; for (j = 0; pd[j].header; j++) { if (header) pd[j].width = strlen(pd[j].header); else pd[j].width = 0; vector_foreach_slot (pathvec, pp, i) { pd[j].snprint(buff, MAX_FIELD_LEN, pp); pd[j].width = MAX(pd[j].width, strlen(buff)); } } } static void reset_multipath_layout (void) { int i; for (i = 0; mpd[i].header; i++) mpd[i].width = 0; } void get_multipath_layout (vector mpvec, int header) { int i, j; char buff[MAX_FIELD_LEN]; struct multipath * mpp; for (j = 0; mpd[j].header; j++) { if (header) mpd[j].width = strlen(mpd[j].header); else mpd[j].width = 0; vector_foreach_slot (mpvec, mpp, i) { mpd[j].snprint(buff, MAX_FIELD_LEN, mpp); mpd[j].width = MAX(mpd[j].width, strlen(buff)); } } } static struct multipath_data * mpd_lookup(char wildcard) { int i; for (i = 0; mpd[i].header; i++) if (mpd[i].wildcard == wildcard) return &mpd[i]; return NULL; } static struct path_data * pd_lookup(char wildcard) { int i; for (i = 0; pd[i].header; i++) if (pd[i].wildcard == wildcard) return &pd[i]; return NULL; } static struct pathgroup_data * pgd_lookup(char wildcard) { int i; for (i = 0; pgd[i].header; i++) if (pgd[i].wildcard == wildcard) return &pgd[i]; return NULL; } int snprint_multipath_header (char * line, int len, char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; memset(line, 0, len); do { if (!TAIL) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = mpd_lookup(*f))) continue; /* unknown wildcard */ PRINT(c, TAIL, "%s", data->header); PAD(data->width); } while (*f++); ENDLINE; return (c - line); } int snprint_multipath (char * line, int len, char * format, struct multipath * mpp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; char buff[MAX_FIELD_LEN] = {}; memset(line, 0, len); do { if (!TAIL) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = mpd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); buff[0] = '\0'; } while (*f++); ENDLINE; return (c - line); } int snprint_path_header (char * line, int len, char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct path_data * data; memset(line, 0, len); do { if (!TAIL) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pd_lookup(*f))) continue; /* unknown wildcard */ PRINT(c, TAIL, "%s", data->header); PAD(data->width); } while (*f++); ENDLINE; return (c - line); } int snprint_path (char * line, int len, char * format, struct path * pp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct path_data * data; char buff[MAX_FIELD_LEN]; memset(line, 0, len); do { if (!TAIL) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, pp); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); } while (*f++); ENDLINE; return (c - line); } int snprint_pathgroup (char * line, int len, char * format, struct pathgroup * pgp) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct pathgroup_data * data; char buff[MAX_FIELD_LEN]; memset(line, 0, len); do { if (!TAIL) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pgd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, pgp); PRINT(c, TAIL, "%s", buff); PAD(data->width); } while (*f++); ENDLINE; return (c - line); } extern void print_multipath_topology (struct multipath * mpp, int verbosity) { int resize; char *buff = NULL; char *old = NULL; int len, maxlen = MAX_LINE_LEN * MAX_LINES; buff = MALLOC(maxlen); do { if (!buff) { if (old) FREE(old); condlog(0, "couldn't allocate memory for list: %s\n", strerror(errno)); return; } len = snprint_multipath_topology(buff, maxlen, mpp, verbosity); resize = (len == maxlen - 1); if (resize) { maxlen *= 2; old = buff; buff = REALLOC(buff, maxlen); } } while (resize); printf("%s", buff); FREE(buff); } extern int snprint_multipath_topology (char * buff, int len, struct multipath * mpp, int verbosity) { int j, i, fwd = 0; struct path * pp = NULL; struct pathgroup * pgp = NULL; char style[64]; char * c = style; char fmt[64]; char * f; if (verbosity <= 0) return fwd; reset_multipath_layout(); if (verbosity == 1) return snprint_multipath(buff, len, "%n", mpp, 1); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */ if (verbosity > 1 && mpp->action != ACT_NOTHING && mpp->action != ACT_UNDEF) c += sprintf(c, "%%A: "); c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) c += sprintf(c, " (%%w)"); c += sprintf(c, " %%d %%s"); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */ fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp, 1); if (fwd > len) return len; fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp, 1); if (fwd > len) return len; if (!mpp->pg) return fwd; vector_foreach_slot (mpp->pg, pgp, j) { f=fmt; pgp->selector = mpp->selector; /* hack */ if (j + 1 < VECTOR_SIZE(mpp->pg)) { strcpy(f, "|-+- " PRINT_PG_INDENT); } else strcpy(f, "`-+- " PRINT_PG_INDENT); fwd += snprint_pathgroup(buff + fwd, len - fwd, fmt, pgp); if (fwd > len) return len; vector_foreach_slot (pgp->paths, pp, i) { f=fmt; if (*f != '|') *f=' '; f++; if (i + 1 < VECTOR_SIZE(pgp->paths)) strcpy(f, " |- " PRINT_PATH_INDENT); else strcpy(f, " `- " PRINT_PATH_INDENT); fwd += snprint_path(buff + fwd, len - fwd, fmt, pp, 1); if (fwd > len) return len; } } return fwd; } static int snprint_hwentry (char * buff, int len, struct hwentry * hwe) { int i; int fwd = 0; struct keyword * kw; struct keyword * rootkw; rootkw = find_keyword(NULL, "devices"); if (!rootkw || !rootkw->sub) return 0; rootkw = find_keyword(rootkw->sub, "device"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd > len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, hwe); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd > len) return len; return fwd; } extern int snprint_hwtable (char * buff, int len, vector hwtable) { int fwd = 0; int i; struct hwentry * hwe; struct keyword * rootkw; rootkw = find_keyword(NULL, "devices"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "devices {\n"); if (fwd > len) return len; vector_foreach_slot (hwtable, hwe, i) { fwd += snprint_hwentry(buff + fwd, len - fwd, hwe); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } static int snprint_mpentry (char * buff, int len, struct mpentry * mpe) { int i; int fwd = 0; struct keyword * kw; struct keyword * rootkw; rootkw = find_keyword(NULL, "multipath"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "\tmultipath {\n"); if (fwd > len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, mpe); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd > len) return len; return fwd; } extern int snprint_mptable (char * buff, int len, vector mptable) { int fwd = 0; int i; struct mpentry * mpe; struct keyword * rootkw; rootkw = find_keyword(NULL, "multipaths"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n"); if (fwd > len) return len; vector_foreach_slot (mptable, mpe, i) { fwd += snprint_mpentry(buff + fwd, len - fwd, mpe); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } extern int snprint_overrides (char * buff, int len, struct hwentry *overrides) { int fwd = 0; int i; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(NULL, "overrides"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "overrides {\n"); if (fwd > len) return len; if (!overrides) goto out; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, NULL); if (fwd > len) return len; } out: fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } extern int snprint_defaults (char * buff, int len) { int fwd = 0; int i; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(NULL, "defaults"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "defaults {\n"); if (fwd > len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, NULL); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } static int snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) { int threshold = MAX_LINE_LEN; struct blentry * ble; int pos; int i; pos = *fwd; if (!VECTOR_SIZE(*vec)) { if ((len - pos - threshold) <= 0) return 0; pos += snprintf(buff + pos, len - pos, " \n"); } else vector_foreach_slot (*vec, ble, i) { if ((len - pos - threshold) <= 0) return 0; if (ble->origin == ORIGIN_CONFIG) pos += snprintf(buff + pos, len - pos, " (config file rule) "); else if (ble->origin == ORIGIN_DEFAULT) pos += snprintf(buff + pos, len - pos, " (default rule) "); pos += snprintf(buff + pos, len - pos, "%s\n", ble->str); } *fwd = pos; return pos; } static int snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec) { int threshold = MAX_LINE_LEN; struct blentry_device * bled; int pos; int i; pos = *fwd; if (!VECTOR_SIZE(*vec)) { if ((len - pos - threshold) <= 0) return 0; pos += snprintf(buff + pos, len - pos, " \n"); } else vector_foreach_slot (*vec, bled, i) { if ((len - pos - threshold) <= 0) return 0; if (bled->origin == ORIGIN_CONFIG) pos += snprintf(buff + pos, len - pos, " (config file rule) "); else if (bled->origin == ORIGIN_DEFAULT) pos += snprintf(buff + pos, len - pos, " (default rule) "); pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product); } *fwd = pos; return pos; } extern int snprint_blacklist_report (char * buff, int len) { int threshold = MAX_LINE_LEN; int fwd = 0; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n" "- blacklist:\n"); if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode)) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n" "- blacklist:\n"); if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property)) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n" "- blacklist:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_wwid) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "device rules:\n" "- blacklist:\n"); if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0) return len; if (fwd > len) return len; return fwd; } extern int snprint_blacklist (char * buff, int len) { int i; struct blentry * ble; struct blentry_device * bled; int fwd = 0; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(NULL, "blacklist"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "blacklist {\n"); if (fwd > len) return len; vector_foreach_slot (conf->blist_devnode, ble, i) { kw = find_keyword(rootkw->sub, "devnode"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd > len) return len; } vector_foreach_slot (conf->blist_wwid, ble, i) { kw = find_keyword(rootkw->sub, "wwid"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd > len) return len; } vector_foreach_slot (conf->blist_property, ble, i) { kw = find_keyword(rootkw->sub, "property"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd > len) return len; } rootkw = find_keyword(rootkw->sub, "device"); if (!rootkw) return 0; vector_foreach_slot (conf->blist_device, bled, i) { fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd > len) return len; kw = find_keyword(rootkw->sub, "vendor"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, bled); if (fwd > len) return len; kw = find_keyword(rootkw->sub, "product"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, bled); if (fwd > len) return len; fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } extern int snprint_blacklist_except (char * buff, int len) { int i; struct blentry * ele; struct blentry_device * eled; int fwd = 0; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(NULL, "blacklist_exceptions"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n"); if (fwd > len) return len; vector_foreach_slot (conf->elist_devnode, ele, i) { kw = find_keyword(rootkw->sub, "devnode"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd > len) return len; } vector_foreach_slot (conf->elist_wwid, ele, i) { kw = find_keyword(rootkw->sub, "wwid"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd > len) return len; } vector_foreach_slot (conf->elist_property, ele, i) { kw = find_keyword(rootkw->sub, "property"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd > len) return len; } rootkw = find_keyword(rootkw->sub, "device"); if (!rootkw) return 0; vector_foreach_slot (conf->elist_device, eled, i) { fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd > len) return len; kw = find_keyword(rootkw->sub, "vendor"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, eled); if (fwd > len) return len; kw = find_keyword(rootkw->sub, "product"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, eled); if (fwd > len) return len; fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd > len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; } extern int snprint_status (char * buff, int len, struct vectors *vecs) { int fwd = 0; int i; unsigned int count[PATH_MAX_STATE] = {0}; struct path * pp; vector_foreach_slot (vecs->pathvec, pp, i) { count[pp->state]++; } fwd += snprintf(buff + fwd, len - fwd, "path checker states:\n"); for (i=0; ipathvec, pp, i) if (pp->fd != -1) monitored_count++; fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n", monitored_count, is_uevent_busy()? "True" : "False"); if (fwd > len) return len; return fwd; } extern int snprint_devices (char * buff, int len, struct vectors *vecs) { DIR *blkdir; struct dirent *blkdev; struct stat statbuf; char devpath[PATH_MAX]; char *devptr; int threshold = MAX_LINE_LEN; int fwd = 0; int r; struct path * pp; if (!(blkdir = opendir("/sys/block"))) return 1; if ((len - fwd - threshold) <= 0) { closedir(blkdir); return len; } fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); strcpy(devpath,"/sys/block/"); while ((blkdev = readdir(blkdir)) != NULL) { if ((strcmp(blkdev->d_name,".") == 0) || (strcmp(blkdev->d_name,"..") == 0)) continue; devptr = devpath + 11; *devptr = '\0'; strncat(devptr, blkdev->d_name, PATH_MAX-12); if (stat(devpath, &statbuf) < 0) continue; if (S_ISDIR(statbuf.st_mode) == 0) continue; if ((len - fwd - threshold) <= 0) { closedir(blkdir); return len; } fwd += snprintf(buff + fwd, len - fwd, " %s", devptr); pp = find_path_by_dev(vecs->pathvec, devptr); if (!pp) { r = filter_devnode(conf->blist_devnode, conf->elist_devnode, devptr); if (r > 0) fwd += snprintf(buff + fwd, len - fwd, " devnode blacklisted, unmonitored"); else if (r <= 0) fwd += snprintf(buff + fwd, len - fwd, " devnode whitelisted, unmonitored"); } else fwd += snprintf(buff + fwd, len - fwd, " devnode whitelisted, monitored"); fwd += snprintf(buff + fwd, len - fwd, "\n"); } closedir(blkdir); if (fwd > len) return len; return fwd; } extern int snprint_config (char * buff, int len) { return 0; } /* * stdout printing helpers */ extern void print_path (struct path * pp, char * style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1); printf("%s", line); } extern void print_multipath (struct multipath * mpp, char * style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp, 1); printf("%s", line); } extern void print_pathgroup (struct pathgroup * pgp, char * style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_pathgroup(&line[0], MAX_LINE_LEN, style, pgp); printf("%s", line); } extern void print_map (struct multipath * mpp, char * params) { if (mpp->size && params) printf("0 %llu %s %s\n", mpp->size, TGT_MPATH, params); return; } extern void print_all_paths (vector pathvec, int banner) { print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG); } extern void print_all_paths_custo (vector pathvec, int banner, char *fmt) { int i; struct path * pp; char line[MAX_LINE_LEN]; if (!VECTOR_SIZE(pathvec)) { if (banner) fprintf(stdout, "===== no paths =====\n"); return; } if (banner) fprintf(stdout, "===== paths list =====\n"); get_path_layout(pathvec, 1); snprint_path_header(line, MAX_LINE_LEN, fmt); fprintf(stdout, "%s", line); vector_foreach_slot (pathvec, pp, i) print_path(pp, fmt); } multipath-tools-0.5.0+git1.656f8865/libmultipath/print.h000066400000000000000000000043301260771327300225540ustar00rootroot00000000000000#define PRINT_PATH_LONG "%w %i %d %D %p %t %T %s %o" #define PRINT_PATH_INDENT "%i %d %D %t %T %o" #define PRINT_PATH_CHECKER "%i %d %D %p %t %T %o %C" #define PRINT_MAP_STATUS "%n %F %Q %N %t %r" #define PRINT_MAP_STATS "%n %0 %1 %2 %3 %4" #define PRINT_MAP_NAMES "%n %d %w" #define PRINT_MAP_PROPS "size=%S features='%f' hwhandler='%h' wp=%r" #define PRINT_PG_INDENT "policy='%s' prio=%p status=%t" #define MAX_LINE_LEN 80 #define MAX_LINES 64 #define MAX_FIELD_LEN 64 #define PROGRESS_LEN 10 struct path_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct path * pp); }; struct multipath_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct multipath * mpp); }; struct pathgroup_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct pathgroup * pgp); }; void get_path_layout (vector pathvec, int header); void get_multipath_layout (vector mpvec, int header); int snprint_path_header (char *, int, char *); int snprint_multipath_header (char *, int, char *); int snprint_path (char *, int, char *, struct path *, int); int snprint_multipath (char *, int, char *, struct multipath *, int); int snprint_multipath_topology (char *, int, struct multipath * mpp, int verbosity); int snprint_defaults (char *, int); int snprint_blacklist (char *, int); int snprint_blacklist_except (char *, int); int snprint_blacklist_report (char *, int); int snprint_wildcards (char *, int); int snprint_status (char *, int, struct vectors *); int snprint_devices (char *, int, struct vectors *); int snprint_hwtable (char *, int, vector); int snprint_mptable (char *, int, vector); int snprint_overrides (char *, int, struct hwentry *); void print_multipath_topology (struct multipath * mpp, int verbosity); void print_path (struct path * pp, char * style); void print_multipath (struct multipath * mpp, char * style); void print_pathgroup (struct pathgroup * pgp, char * style); void print_map (struct multipath * mpp, char * params); void print_all_paths (vector pathvec, int banner); void print_all_paths_custo (vector pathvec, int banner, char *fmt); void print_hwtable (vector hwtable); multipath-tools-0.5.0+git1.656f8865/libmultipath/prio.c000066400000000000000000000063151260771327300223710ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "prio.h" #include "config.h" static LIST_HEAD(prioritizers); unsigned int get_prio_timeout(unsigned int default_timeout) { if (conf->checker_timeout) return conf->checker_timeout * 1000; return default_timeout; } int init_prio (void) { if (!add_prio(DEFAULT_PRIO)) return 1; return 0; } static struct prio * alloc_prio (void) { struct prio *p; p = MALLOC(sizeof(struct prio)); if (p) { INIT_LIST_HEAD(&p->node); p->refcount = 1; } return p; } void free_prio (struct prio * p) { if (!p) return; p->refcount--; if (p->refcount) { condlog(3, "%s prioritizer refcount %d", p->name, p->refcount); return; } condlog(3, "unloading %s prioritizer", p->name); list_del(&p->node); if (p->handle) { if (dlclose(p->handle) != 0) { condlog(0, "Cannot unload prioritizer %s: %s", p->name, dlerror()); } } FREE(p); } void cleanup_prio(void) { struct prio * prio_loop; struct prio * prio_temp; list_for_each_entry_safe(prio_loop, prio_temp, &prioritizers, node) { free_prio(prio_loop); } } struct prio * prio_lookup (char * name) { struct prio * p; if (!name || !strlen(name)) return NULL; list_for_each_entry(p, &prioritizers, node) { if (!strncmp(name, p->name, PRIO_NAME_LEN)) return p; } return add_prio(name); } int prio_set_args (struct prio * p, char * args) { return snprintf(p->args, PRIO_ARGS_LEN, "%s", args); } struct prio * add_prio (char * name) { char libname[LIB_PRIO_NAMELEN]; struct stat stbuf; struct prio * p; char *errstr; p = alloc_prio(); if (!p) return NULL; snprintf(p->name, PRIO_NAME_LEN, "%s", name); snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so", conf->multipath_dir, name); if (stat(libname,&stbuf) < 0) { condlog(0,"Prioritizer '%s' not found in %s", name, conf->multipath_dir); goto out; } condlog(3, "loading %s prioritizer", libname); p->handle = dlopen(libname, RTLD_NOW); if (!p->handle) { if ((errstr = dlerror()) != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); goto out; } p->getprio = (int (*)(struct path *, char *)) dlsym(p->handle, "getprio"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!p->getprio) goto out; list_add(&p->node, &prioritizers); return p; out: free_prio(p); return NULL; } int prio_getprio (struct prio * p, struct path * pp) { return p->getprio(pp, p->args); } int prio_selected (struct prio * p) { if (!p) return 0; return (p->getprio) ? 1 : 0; } char * prio_name (struct prio * p) { return p->name; } char * prio_args (struct prio * p) { return p->args; } void prio_get (struct prio * dst, char * name, char * args) { struct prio * src = prio_lookup(name); if (!src) { dst->getprio = NULL; return; } strncpy(dst->name, src->name, PRIO_NAME_LEN); if (args) strncpy(dst->args, args, PRIO_ARGS_LEN); dst->getprio = src->getprio; dst->handle = NULL; src->refcount++; } void prio_put (struct prio * dst) { struct prio * src; if (!dst || !dst->getprio) return; src = prio_lookup(dst->name); memset(dst, 0x0, sizeof(struct prio)); free_prio(src); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prio.h000066400000000000000000000027011260771327300223710ustar00rootroot00000000000000#ifndef _PRIO_H #define _PRIO_H /* * knowing about path struct gives flexibility to prioritizers */ #include "checkers.h" #include "vector.h" /* forward declaration to avoid circular dependency */ struct path; #include "list.h" #include "memory.h" #define DEFAULT_PRIO "const" #define DEFAULT_PRIO_ARGS "" /* * Known prioritizers for use in hwtable.c */ #define PRIO_ALUA "alua" #define PRIO_CONST "const" #define PRIO_EMC "emc" #define PRIO_HDS "hds" #define PRIO_HP_SW "hp_sw" #define PRIO_ONTAP "ontap" #define PRIO_RANDOM "random" #define PRIO_RDAC "rdac" #define PRIO_DATACORE "datacore" #define PRIO_WEIGHTED_PATH "weightedpath" /* * Value used to mark the fact prio was not defined */ #define PRIO_UNDEF -1 /* * strings lengths */ #define LIB_PRIO_NAMELEN 255 #define PRIO_NAME_LEN 16 #define PRIO_ARGS_LEN 255 struct prio { void *handle; int refcount; struct list_head node; char name[PRIO_NAME_LEN]; char args[PRIO_ARGS_LEN]; int (*getprio)(struct path *, char *); }; unsigned int get_prio_timeout(unsigned int default_timeout); int init_prio (void); void cleanup_prio (void); struct prio * add_prio (char *); struct prio * prio_lookup (char *); int prio_getprio (struct prio *, struct path *); void prio_get (struct prio *, char *, char *); void prio_put (struct prio *); int prio_selected (struct prio *); char * prio_name (struct prio *); char * prio_args (struct prio *); int prio_set_args (struct prio *, char *); #endif /* _PRIO_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/000077500000000000000000000000001260771327300240145ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/Makefile000066400000000000000000000012711260771327300254550ustar00rootroot00000000000000# Makefile # # Copyright (C) 2007 Christophe Varoqui, # include ../../Makefile.inc LIBS = \ libpriorandom.so \ libprioconst.so \ libpriohp_sw.so \ libprioemc.so \ libpriordac.so \ libprioalua.so \ libprioontap.so \ libpriodatacore.so \ libpriohds.so \ libprioweightedpath.so \ libprioiet.so CFLAGS += -I.. all: $(LIBS) libprioalua.so: alua.o alua_rtpg.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ libprio%.so: %.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ install: $(LIBS) $(INSTALL_PROGRAM) -m 755 libprio*.so $(DESTDIR)$(libdir) uninstall: for file in $(LIBS); do rm -f $(DESTDIR)$(libdir)/$$file; done clean: rm -f core *.a *.o *.gz *.so multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/alua.c000066400000000000000000000052771260771327300251150ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * main.c * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #include #include #include #include #include "alua.h" #define ALUA_PRIO_NOT_SUPPORTED 1 #define ALUA_PRIO_RTPG_FAILED 2 #define ALUA_PRIO_GETAAS_FAILED 3 #define ALUA_PRIO_TPGS_FAILED 4 #define ALUA_PRIO_NO_INFORMATION 5 static const char * aas_string[] = { [AAS_OPTIMIZED] = "active/optimized", [AAS_NON_OPTIMIZED] = "active/non-optimized", [AAS_STANDBY] = "standby", [AAS_UNAVAILABLE] = "unavailable", [AAS_LBA_DEPENDENT] = "lba dependent", [AAS_RESERVED] = "invalid/reserved", [AAS_OFFLINE] = "offline", [AAS_TRANSITIONING] = "transitioning between states", }; static const char *aas_print_string(int rc) { rc &= 0x7f; if (rc & 0x70) return aas_string[AAS_RESERVED]; rc &= 0x0f; if (rc > AAS_RESERVED && rc < AAS_OFFLINE) return aas_string[AAS_RESERVED]; else return aas_string[rc]; } int get_alua_info(int fd) { int rc; int tpg; rc = get_target_port_group_support(fd); if (rc < 0) return -ALUA_PRIO_TPGS_FAILED; if (rc == TPGS_NONE) return -ALUA_PRIO_NOT_SUPPORTED; tpg = get_target_port_group(fd); if (tpg < 0) return -ALUA_PRIO_RTPG_FAILED; condlog(3, "reported target port group is %i", tpg); rc = get_asymmetric_access_state(fd, tpg); if (rc < 0) return -ALUA_PRIO_GETAAS_FAILED; condlog(3, "aas = %02x [%s]%s", rc, aas_print_string(rc), (rc & 0x80) ? " [preferred]" : ""); return rc; } int getprio (struct path * pp, char * args) { int rc; int aas; int priopath; if (pp->fd < 0) return -ALUA_PRIO_NO_INFORMATION; rc = get_alua_info(pp->fd); if (rc >= 0) { aas = (rc & 0x0f); priopath = (rc & 0x80); switch(aas) { case AAS_OPTIMIZED: rc = 50; break; case AAS_NON_OPTIMIZED: rc = 10; break; case AAS_LBA_DEPENDENT: rc = 5; break; case AAS_STANDBY: rc = 1; break; default: rc = 0; } if (priopath && aas != AAS_OPTIMIZED) rc += 80; } else { switch(-rc) { case ALUA_PRIO_NOT_SUPPORTED: condlog(0, "%s: alua not supported", pp->dev); break; case ALUA_PRIO_RTPG_FAILED: condlog(0, "%s: couldn't get target port group", pp->dev); break; case ALUA_PRIO_GETAAS_FAILED: condlog(0, "%s: couldn't get asymmetric access state", pp->dev); break; case ALUA_PRIO_TPGS_FAILED: condlog(3, "%s: couldn't get supported alua states", pp->dev); break; } } return rc; } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/alua.h000066400000000000000000000001731260771327300251100ustar00rootroot00000000000000#ifndef _ALUA_H #define _ALUA_H #include "alua_rtpg.h" #define PRIO_ALUA "alua" int prio_alua(struct path * pp); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/alua_rtpg.c000066400000000000000000000160471260771327300261460ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * rtpg.c * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #include #include #include #include #include #define __user #include #include "../prio.h" #include "alua_rtpg.h" #define SENSE_BUFF_LEN 32 #define DEF_TIMEOUT 60000 /* * Macro used to print debug messaged. */ #if DEBUG > 0 #define PRINT_DEBUG(f, a...) \ fprintf(stderr, "DEBUG: " f, ##a) #else #define PRINT_DEBUG(f, a...) #endif /* * Optionally print the commands sent and the data received a hex dump. */ #if DEBUG > 0 #if DEBUG_DUMPHEX > 0 #define PRINT_HEX(p, l) print_hex(p, l) void print_hex(unsigned char *p, unsigned long len) { int i; for(i = 0; i < len; i++) { if (i % 16 == 0) printf("%04x: ", i); printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); } printf("\n"); } #else #define PRINT_HEX(p, l) #endif #else #define PRINT_HEX(p, l) #endif /* * Returns 0 if the SCSI command either was successful or if the an error was * recovered, otherwise 1. (definitions taken from sg_err.h) */ #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 static int scsi_error(struct sg_io_hdr *hdr) { /* Treat SG_ERR here to get rid of sg_err.[ch] */ hdr->status &= 0x7e; if ( (hdr->status == 0) && (hdr->host_status == 0) && (hdr->driver_status == 0) ) { return 0; } if ( (hdr->status == SCSI_CHECK_CONDITION) || (hdr->status == SCSI_COMMAND_TERMINATED) || ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) ) { if (hdr->sbp && (hdr->sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = hdr->sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if (sense_key == RECOVERED_ERROR) return 0; } } return 1; } /* * Helper function to setup and run a SCSI inquiry command. */ int do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) { struct inquiry_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_INQUIRY; if (evpd) { inquiry_command_set_evpd(&cmd); cmd.page = codepage; } set_uint16(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.sbp = sense; hdr.mx_sb_len = sizeof(sense); hdr.timeout = get_prio_timeout(DEF_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) { PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); return -RTPG_INQUIRY_FAILED; } if (scsi_error(&hdr)) { PRINT_DEBUG("do_inquiry: SCSI error!\n"); return -RTPG_INQUIRY_FAILED; } PRINT_HEX((unsigned char *) resp, resplen); return 0; } /* * This function returns the support for target port groups by evaluating the * data returned by the standard inquiry command. */ int get_target_port_group_support(int fd) { struct inquiry_data inq; int rc; memset((unsigned char *)&inq, 0, sizeof(inq)); rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq)); if (!rc) { rc = inquiry_data_get_tpgs(&inq); } return rc; } int get_target_port_group(int fd) { unsigned char *buf; struct vpd83_data * vpd83; struct vpd83_dscr * dscr; int rc; int buflen, scsi_buflen; buflen = 128; /* Lets start from 128 */ buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" "%u bytes\n", buflen); return -RTPG_RTPG_FAILED; } memset(buf, 0, buflen); rc = do_inquiry(fd, 1, 0x83, buf, buflen); if (rc < 0) goto out; scsi_buflen = (buf[2] << 8 | buf[3]) + 4; if (buflen < scsi_buflen) { free(buf); buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" "%u bytes\n", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; memset(buf, 0, buflen); rc = do_inquiry(fd, 1, 0x83, buf, buflen); if (rc < 0) goto out; } vpd83 = (struct vpd83_data *) buf; rc = -RTPG_NO_TPG_IDENTIFIER; FOR_EACH_VPD83_DSCR(vpd83, dscr) { if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { struct vpd83_tpg_dscr *p; if (rc != -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: more " "than one TPG identifier found!\n"); continue; } p = (struct vpd83_tpg_dscr *)dscr->data; rc = get_uint16(p->tpg); } } if (rc == -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: " "no TPG identifier found!\n"); } out: free(buf); return rc; } int do_rtpg(int fd, void* resp, long resplen) { struct rtpg_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_RTPG; rtpg_command_set_service_action(&cmd); set_uint32(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.mx_sb_len = sizeof(sense); hdr.sbp = sense; hdr.timeout = get_prio_timeout(DEF_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) return -RTPG_RTPG_FAILED; if (scsi_error(&hdr)) { PRINT_DEBUG("do_rtpg: SCSI error!\n"); return -RTPG_RTPG_FAILED; } PRINT_HEX(resp, resplen); return 0; } int get_asymmetric_access_state(int fd, unsigned int tpg) { unsigned char *buf; struct rtpg_data * tpgd; struct rtpg_tpg_dscr * dscr; int rc; int buflen; uint32_t scsi_buflen; buflen = 128; /* Initial value from old code */ buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" "%u bytes\n", buflen); return -RTPG_RTPG_FAILED; } memset(buf, 0, buflen); rc = do_rtpg(fd, buf, buflen); if (rc < 0) goto out; scsi_buflen = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) + 4; if (buflen < scsi_buflen) { free(buf); buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" "%u bytes\n", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; memset(buf, 0, buflen); rc = do_rtpg(fd, buf, buflen); if (rc < 0) goto out; } tpgd = (struct rtpg_data *) buf; rc = -RTPG_TPG_NOT_FOUND; RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { if (get_uint16(dscr->tpg) == tpg) { if (rc != -RTPG_TPG_NOT_FOUND) { PRINT_DEBUG("get_asymmetric_access_state: " "more than one entry with same port " "group.\n"); } else { PRINT_DEBUG("pref=%i\n", dscr->b0); rc = rtpg_tpg_dscr_get_aas(dscr); } } } out: free(buf); return rc; } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/alua_rtpg.h000066400000000000000000000013461260771327300261470ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * rtpg.h * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #ifndef __RTPG_H__ #define __RTPG_H__ #include "alua_spc3.h" #define RTPG_SUCCESS 0 #define RTPG_INQUIRY_FAILED 1 #define RTPG_NO_TPG_IDENTIFIER 2 #define RTPG_RTPG_FAILED 3 #define RTPG_TPG_NOT_FOUND 4 int get_target_port_group_support(int fd); int get_target_port_group(int fd); int get_asymmetric_access_state(int fd, unsigned int tpg); #endif /* __RTPG_H__ */ multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/alua_spc3.h000066400000000000000000000241541260771327300260450ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * spc3.h * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #ifndef __SPC3_H__ #define __SPC3_H__ /*============================================================================= * Some helper functions for getting and setting 16 and 32 bit values. *============================================================================= */ static inline unsigned short get_uint16(unsigned char *p) { return (p[0] << 8) + p[1]; } static inline void set_uint16(unsigned char *p, unsigned short v) { p[0] = (v >> 8) & 0xff; p[1] = v & 0xff; } static inline unsigned int get_uint32(unsigned char *p) { return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; } static inline void set_uint32(unsigned char *p, unsigned int v) { p[0] = (v >> 24) & 0xff; p[1] = (v >> 16) & 0xff; p[2] = (v >> 8) & 0xff; p[3] = v & 0xff; } /*============================================================================= * Definitions to support the standard inquiry command as defined in SPC-3. * If the evpd (enable vital product data) bit is set the data that will be * returned is selected by the page field. This field must be 0 if the evpd * bit is not set. *============================================================================= */ #define OPERATION_CODE_INQUIRY 0x12 struct inquiry_command { unsigned char op; unsigned char b1; /* xxxxxx.. = reserved */ /* ......x. = obsolete */ /* .......x = evpd */ unsigned char page; unsigned char length[2]; unsigned char control; } __attribute__((packed)); static inline void inquiry_command_set_evpd(struct inquiry_command *ic) { ic->b1 |= 1; } /*----------------------------------------------------------------------------- * Data returned by the standard inquiry command. *----------------------------------------------------------------------------- * * Peripheral qualifier codes. */ #define PQ_CONNECTED 0x0 #define PQ_DISCONNECTED 0x1 #define PQ_UNSUPPORTED 0x3 /* Defined peripheral device types. */ #define PDT_DIRECT_ACCESS 0x00 #define PDT_SEQUENTIAL_ACCESS 0x01 #define PDT_PRINTER 0x02 #define PDT_PROCESSOR 0x03 #define PDT_WRITE_ONCE 0x04 #define PDT_CD_DVD 0x05 #define PDT_SCANNER 0x06 #define PDT_OPTICAL_MEMORY 0x07 #define PDT_MEDIUM_CHANGER 0x08 #define PDT_COMMUNICATIONS 0x09 #define PDT_STORAGE_ARRAY_CONTROLLER 0x0c #define PDT_ENCLOSURE_SERVICES 0x0d #define PDT_SIMPLIFIED_DIRECT_ACCESS 0x0e #define PDT_OPTICAL_CARD_READER_WRITER 0x0f #define PDT_BRIDGE_CONTROLLER 0x10 #define PDT_OBJECT_BASED 0x11 #define PDT_AUTOMATION_INTERFACE 0x12 #define PDT_LUN 0x1e #define PDT_UNKNOWN 0x1f /* Defined version codes. */ #define VERSION_NONE 0x00 #define VERSION_SPC 0x03 #define VERSION_SPC2 0x04 #define VERSION_SPC3 0x05 /* Defined TPGS field values. */ #define TPGS_NONE 0x0 #define TPGS_IMPLICIT 0x1 #define TPGS_EXPLICIT 0x2 #define TPGS_BOTH 0x3 struct inquiry_data { unsigned char b0; /* xxx..... = peripheral_qualifier */ /* ...xxxxx = peripheral_device_type */ unsigned char b1; /* x....... = removable medium */ /* .xxxxxxx = reserverd */ unsigned char version; unsigned char b3; /* xx...... = obsolete */ /* ..x..... = normal aca supported */ /* ...x.... = hirarchichal lun supp. */ /* ....xxxx = response format */ /* 2 is spc-3 format */ unsigned char length; unsigned char b5; /* x....... = storage controller */ /* component supported */ /* .x...... = access controls coord. */ /* ..xx.... = target port group supp.*/ /* ....x... = third party copy supp. */ /* .....xx. = reserved */ /* .......x = protection info supp. */ unsigned char b6; /* x....... = bque */ /* .x...... = enclosure services sup.*/ /* ..x..... = vs1 */ /* ...x.... = multiport support */ /* ....x... = medium changer */ /* .....xx. = obsolete */ /* .......x = add16 */ unsigned char b7; /* xx...... = obsolete */ /* ..x..... = wbus16 */ /* ...x.... = sync */ /* ....x... = linked commands supp. */ /* .....x.. = obsolete */ /* ......x. = command queue support */ /* .......x = vs2 */ unsigned char vendor_identification[8]; unsigned char product_identification[16]; unsigned char product_revision[4]; unsigned char vendor_specific[20]; unsigned char b56; /* xxxx.... = reserved */ /* ....xx.. = clocking */ /* ......x. = qas */ /* .......x = ius */ unsigned char reserved4; unsigned char version_descriptor[8][2]; unsigned char reserved5[22]; unsigned char vendor_parameters[0]; } __attribute__((packed)); static inline int inquiry_data_get_tpgs(struct inquiry_data *id) { return (id->b5 >> 4) & 3; } /*----------------------------------------------------------------------------- * Inquiry data returned when requesting vital product data page 0x83. *----------------------------------------------------------------------------- */ #define CODESET_BINARY 0x1 #define CODESET_ACSII 0x2 #define CODESET_UTF8 0x3 #define ASSOCIATION_UNIT 0x0 #define ASSOCIATION_PORT 0x1 #define ASSOCIATION_DEVICE 0x2 #define IDTYPE_VENDOR_SPECIFIC 0x0 #define IDTYPE_T10_VENDOR_ID 0x1 #define IDTYPE_EUI64 0x2 #define IDTYPE_NAA 0x3 #define IDTYPE_RELATIVE_TPG_ID 0x4 #define IDTYPE_TARGET_PORT_GROUP 0x5 #define IDTYPE_LUN_GROUP 0x6 #define IDTYPE_MD5_LUN_ID 0x7 #define IDTYPE_SCSI_NAME_STRING 0x8 struct vpd83_tpg_dscr { unsigned char reserved1[2]; unsigned char tpg[2]; } __attribute__((packed)); struct vpd83_dscr { unsigned char b0; /* xxxx.... = protocol id */ /* ....xxxx = codeset */ unsigned char b1; /* x....... = protocol id valid */ /* .x...... = reserved */ /* ..xx.... = association */ /* ....xxxx = id type */ unsigned char reserved2; unsigned char length; /* size-4 */ unsigned char data[0]; } __attribute__((packed)); static inline int vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) { return ((d->b1 & 7) == type); } struct vpd83_data { unsigned char b0; /* xxx..... = peripheral_qualifier */ /* ...xxxxx = peripheral_device_type */ unsigned char page_code; /* 0x83 */ unsigned char length[2]; /* size-4 */ struct vpd83_dscr data[0]; } __attribute__((packed)); /*----------------------------------------------------------------------------- * This macro should be used to walk through all identification descriptors * defined in the code page 0x83. * The argument p is a pointer to the code page 0x83 data and d is used to * point to the current descriptor. *----------------------------------------------------------------------------- */ #define FOR_EACH_VPD83_DSCR(p, d) \ for( \ d = p->data; \ (((char *) d) - ((char *) p)) < \ get_uint16(p->length); \ d = (struct vpd83_dscr *) \ ((char *) d + d->length + 4) \ ) /*============================================================================= * The following stuctures and macros are used to call the report target port * groups command defined in SPC-3. * This command is used to get information about the target port groups (which * states are supported, which ports belong to this group, and so on) and the * current state of each target port group. *============================================================================= */ #define OPERATION_CODE_RTPG 0xa3 #define SERVICE_ACTION_RTPG 0x0a struct rtpg_command { unsigned char op; /* 0xa3 */ unsigned char b1; /* xxx..... = reserved */ /* ...xxxxx = service action (0x0a) */ unsigned char reserved2[4]; unsigned char length[4]; unsigned char reserved3; unsigned char control; } __attribute__((packed)); static inline void rtpg_command_set_service_action(struct rtpg_command *cmd) { cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG; } struct rtpg_tp_dscr { unsigned char obsolete1[2]; /* The Relative Target Port Identifier of a target port. */ unsigned char rtpi[2]; } __attribute__((packed)); #define AAS_OPTIMIZED 0x0 #define AAS_NON_OPTIMIZED 0x1 #define AAS_STANDBY 0x2 #define AAS_UNAVAILABLE 0x3 #define AAS_LBA_DEPENDENT 0x4 #define AAS_RESERVED 0x5 #define AAS_OFFLINE 0xe #define AAS_TRANSITIONING 0xf #define TPG_STATUS_NONE 0x0 #define TPG_STATUS_SET 0x1 #define TPG_STATUS_IMPLICIT_CHANGE 0x2 struct rtpg_tpg_dscr { unsigned char b0; /* x....... = pref(ered) port */ /* .xxx.... = reserved */ /* ....xxxx = asymetric access state */ unsigned char b1; /* xxx..... = reserved */ /* ...x.... = LBA dependent support */ /* ....x... = unavailable support */ /* .....x.. = standby support */ /* ......x. = non-optimized support */ /* .......x = optimized support */ unsigned char tpg[2]; unsigned char reserved3; unsigned char status; unsigned char vendor_unique; unsigned char port_count; struct rtpg_tp_dscr data[0]; } __attribute__((packed)); static inline int rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d) { return (d->b0 & 0x8f); } struct rtpg_data { unsigned char length[4]; /* size-4 */ struct rtpg_tpg_dscr data[0]; } __attribute__((packed)); #define RTPG_FOR_EACH_PORT_GROUP(p, g) \ for( \ g = &(p->data[0]); \ (((char *) g) - ((char *) p)) < get_uint32(p->length); \ g = (struct rtpg_tpg_dscr *) ( \ ((char *) g) + \ sizeof(struct rtpg_tpg_dscr) + \ g->port_count * sizeof(struct rtpg_tp_dscr) \ ) \ ) #endif /* __SPC3_H__ */ multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/const.c000066400000000000000000000001421260771327300253030ustar00rootroot00000000000000#include #include int getprio (struct path * pp, char * args) { return 1; } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/datacore.c000066400000000000000000000053701260771327300257470ustar00rootroot00000000000000/* * (C) 2010 Christophe Varoqui * (C) 2009 Dembach Goo Infromatik GmbH & Co KG * Manon Goo * * datacore.c * Version 0.9 * * This program was inspired by work from * Matthias Rudolph * * This work is made available on the basis of the * GPLv2 for detials see . * * Manon Goo 2009 * * */ #include #include #include #include #include #include #include #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 #define INQ_CMD_LEN 6 #define dc_log(prio, msg) condlog(prio, "%s: datacore prio: " msg, dev) int datacore_prio (const char *dev, int sg_fd, char * args) { int k; char vendor[8]; char product[32]; char luname[32]; char wwpn[32]; char sdsname[32]; unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; unsigned char inqBuff[INQ_REPLY_LEN]; unsigned char *inqBuffp = inqBuff; unsigned char sense_buffer[32]; sg_io_hdr_t io_hdr; int timeout = 2000; char preferredsds_buff[255] = ""; char * preferredsds = &preferredsds_buff[0]; if (!args) { dc_log(0, "need prio_args with preferredsds set"); return 0; } if (sscanf(args, "timeout=%i preferredsds=%s", &timeout, preferredsds) == 2) {} else if (sscanf(args, "preferredsds=%s timeout=%i", preferredsds, &timeout) == 2) {} else if (sscanf(args, "preferredsds=%s", preferredsds) == 1) {} else { dc_log(0, "unexpected prio_args format"); return 0; } // on error just return prio 0 if (strlen(preferredsds) <= 1) { dc_log(0, "prio args: preferredsds too short (1 character min)"); return 0; } if ((timeout < 500) || (timeout > 20000)) { dc_log(0, "prio args: timeout out of bounds [500:20000]"); return 0; } if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) return 0; memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = INQ_REPLY_LEN; io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout; // on error just return prio 0 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 0; if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) return 0; snprintf(vendor, 8, "%.8s\n", inqBuffp + 8); snprintf(product, 17, "%.16s", inqBuffp + 16); snprintf(luname, 21, "%.19s", inqBuffp + 36); snprintf(wwpn, 17, "%.16s", inqBuffp + 96); snprintf(sdsname, 17, "%.16s", inqBuffp + 112); if (strstr(sdsname , preferredsds)) return 1; return 0; } int getprio (struct path * pp, char * args) { return datacore_prio(pp->dev, pp->fd, args); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/emc.c000066400000000000000000000044041260771327300247260ustar00rootroot00000000000000#include #include #include #include #include #include #include #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define pp_emc_log(prio, msg) condlog(prio, "%s: emc prio: " msg, dev) int emc_clariion_prio(const char *dev, int fd) { unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = PRIO_UNDEF; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(&sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_emc_log(0, "sending query command failed"); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_emc_log(0, "query command indicates error"); goto out; } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { pp_emc_log(0, "path unit report page in unknown format"); goto out; } if ( /* Effective initiator type */ sense_buffer[27] != 0x03 /* * Failover mode should be set to 1 (PNR failover mode) * or 4 (ALUA failover mode). */ || (((sense_buffer[28] & 0x07) != 0x04) && ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { pp_emc_log(0, "path not correctly configured for failover"); goto out; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { pp_emc_log(0, "path not available for normal operations"); goto out; } /* LUN state: unbound, bound, or owned */ ret = sense_buffer[4]; /* Is the default owner equal to this path? */ /* Note this will switch to the default priority group, even if * it is not the currently active one. */ if (sense_buffer[5] == sense_buffer[8]) ret+=2; out: return(ret); } int getprio (struct path * pp, char * args) { return emc_clariion_prio(pp->dev, pp->fd); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/hds.c000066400000000000000000000130021260771327300247320ustar00rootroot00000000000000/* * (C) Copyright HDS GmbH 2006. All Rights Reserved. * * pp_hds_modular.c * Version 2.00 * * Prioritizer for Device Mapper Multipath and HDS Storage * * Hitachis Modular Storage contains two controllers for redundancy. The * Storage internal LUN (LDEV) will normally allocated via two pathes to the * server (one path per controller). For performance reasons should the server * access to a LDEV only via one controller. The other path to the other * controller is stand-by. It is also possible to allocate more as one path * for a LDEV per controller. Here is active/active access allowed. The other * pathes via the other controller are stand-by. * * This prioritizer checks with inquiry command the represented LDEV and * Controller number and gives back a priority followed by this scheme: * * CONTROLLER ODD and LDEV ODD: PRIORITY 1 * CONTROLLER ODD and LDEV EVEN: PRIORITY 0 * CONTROLLER EVEN and LDEV ODD: PRIORITY 0 * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1 * * In the storage you can define for each LDEV a owner controller. If the * server makes IOs via the other controller the storage will switch the * ownership automatically. In this case you can see in the storage that the * current controller is different from the default controller, but this is * absolutely no problem. * * With this prioritizer it is possible to establish a static load balancing. * Half of the LUNs are accessed via one HBA/storage controller and the other * half via the other HBA/storage controller. * * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have * access to the LDEVs via the same controller. * * You can run the prioritizer manually in verbose mode: * # pp_hds_modular -v 8:224 * VENDOR: HITACHI * PRODUCT: DF600F-CM * SERIAL: 0x0105 * LDEV: 0x00C6 * CTRL: 1 * PORT: B * CTRL ODD, LDEV EVEN, PRIO 0 * * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular * * Changes 2006-07-16: * - Changed to forward declaration of functions * - The switch-statement was changed to a logical expression * - unlinking of the devpath now also occurs at the end of * hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode * entries in /tmp-Directory * - The for-statements for passing variables where changed to * snprintf-commands in verbose mode * Changes 2006-08-10: * - Back to the old switch statements because the regular expression does * not work under RHEL4 U3 i386 * Changes 2007-06-27: * - switched from major:minor argument to device node argument * * This file is released under the GPL. * */ #include #include #include #include #include #include #include #include #include #include #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 #define INQ_CMD_LEN 6 #define pp_hds_log(prio, fmt, args...) \ condlog(prio, "%s: hds prio: " fmt, dev, ##args) int hds_modular_prio (const char *dev, int fd) { int k; char vendor[9]; char product[32]; char serial[32]; char ldev[32]; char ctrl[32]; char port[32]; unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; unsigned char inqBuff[INQ_REPLY_LEN]; unsigned char *inqBuffp = inqBuff; unsigned char sense_buffer[32]; sg_io_hdr_t io_hdr; if ((ioctl (fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { pp_hds_log(0, "can't use SG ioctl interface"); return -1; } memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); memset (inqBuff, 0, INQ_REPLY_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = INQ_REPLY_LEN; io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = get_prio_timeout(2000); /* TimeOut = 2 seconds */ if (ioctl (fd, SG_IO, &io_hdr) < 0) { pp_hds_log(0, "SG_IO error"); return -1; } if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { pp_hds_log(0, "SCSI error"); return -1; } snprintf (vendor, 9, "%.8s", inqBuffp + 8); snprintf (product, 17, "%.16s", inqBuffp + 16); snprintf (serial, 5, "%.4s", inqBuffp + 40); snprintf (ldev, 5, "%.4s", inqBuffp + 44); snprintf (ctrl, 2, "%.1s", inqBuffp + 49); snprintf (port, 2, "%.1s", inqBuffp + 50); pp_hds_log(4, "VENDOR: %s", vendor); pp_hds_log(4, "PRODUCT: %s", product); pp_hds_log(4, "SERIAL: 0x%s", serial); pp_hds_log(4, "LDEV: 0x%s", ldev); pp_hds_log(4, "CTRL: %s", ctrl); pp_hds_log(4, "PORT: %s", port); switch (ctrl[0]) { case '0': case '2': case '4': case '6': case '8': switch (ldev[3]) { case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': pp_hds_log(4, "CTRL EVEN, LDEV EVEN, PRIO 1"); return 1; break; case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': pp_hds_log(4, "CTRL EVEN, LDEV ODD, PRIO 0"); return 0; break; } case '1': case '3': case '5': case '7': case '9': switch (ldev[3]) { case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': pp_hds_log(4, "CTRL ODD, LDEV EVEN, PRIO 0"); return 0; break; case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': pp_hds_log(4, "CTRL ODD, LDEV ODD, PRIO 1"); return 1; break; } } return -1; } int getprio (struct path * pp, char * args) { return hds_modular_prio(pp->dev, pp->fd); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/hp_sw.c000066400000000000000000000054451260771327300253100ustar00rootroot00000000000000/* * Path priority checker for HP active/standby controller * * Check the path state and sort them into groups. * There is actually a preferred path in the controller; * we should ask HP on how to retrieve that information. */ #include #include #include #include #include #include #include #include #include #include #define TUR_CMD_LEN 6 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define NOT_READY 0x02 #define UNIT_ATTENTION 0x06 #define HP_PATH_ACTIVE 0x04 #define HP_PATH_STANDBY 0x02 #define HP_PATH_FAILED 0x00 #define pp_hp_sw_log(prio, fmt, args...) \ condlog(prio, "%s: hp_sw prio: " fmt, dev, ##args) int hp_sw_prio(const char *dev, int fd) { unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; unsigned char sb[128]; struct sg_io_hdr io_hdr; int ret = HP_PATH_FAILED; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; retry: if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_hp_sw_log(0, "sending tur command failed"); goto out; } io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) { /* Command completed normally, path is active */ ret = HP_PATH_ACTIVE; } if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key, asc, asq; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) { sense_key = sense_buffer[1] & 0xf; asc = sense_buffer[2]; asq = sense_buffer[3]; } else { sense_key = sense_buffer[2] & 0xf; asc = sense_buffer[12]; asq = sense_buffer[13]; } if(RECOVERED_ERROR == sense_key) ret = HP_PATH_ACTIVE; if(NOT_READY == sense_key) { if (asc == 0x04 && asq == 0x02) { /* This is a standby path */ ret = HP_PATH_STANDBY; } } if(UNIT_ATTENTION == sense_key) { if (asc == 0x29) { /* Retry for device reset */ goto retry; } } } } out: return(ret); } int getprio (struct path * pp, char * args) { return hp_sw_prio(pp->dev, pp->fd); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/iet.c000066400000000000000000000067611260771327300247530ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include // // This prioritizer suits iSCSI needs, makes it possible to prefer one path. // // (It's a bit of a misnomer since supports the client side [eg. open-iscsi] // instead of just "iet".) // // Usage: // prio "iet" // prio_args "preferredip=10.11.12.13" // // Uses /dev/disk/by-path to find the IP of the device. // Assigns prio 20 (high) to the preferred IP and prio 10 (low) to the rest. // // by Olivier Lambert // #define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, dev) // // name: find_regex // @param string: string you want to search into // @param regex: the pattern used // @return result: string finded in string with regex, "none" if none char *find_regex(char * string, char * regex) { int err; regex_t preg; err = regcomp(&preg, regex, REG_EXTENDED); if (err == 0) { int match; size_t nmatch = 0; regmatch_t *pmatch = NULL; nmatch = preg.re_nsub; pmatch = malloc(sizeof(*pmatch) * nmatch); if (pmatch) { match = regexec(&preg, string, nmatch, pmatch, 0); regfree(&preg); if (match == 0) { char *result = NULL; int start = pmatch[0].rm_so; int end = pmatch[0].rm_eo; size_t size = end - start; result = malloc (sizeof(*result) * (size + 1)); if (result) { strncpy(result, &string[start], size); result[size] = '\0'; return result; } } else return NULL; } } return NULL; } // // name: inet_prio // @param // @return prio int iet_prio(const char *dev, char * args) { char preferredip_buff[255] = ""; char *preferredip = &preferredip_buff[0]; // Phase 1 : checks. If anyone fails, return prio 0. // check if args exists if (!args) { dc_log(0, "need prio_args with preferredip set"); return 0; } // check if args format is OK if (sscanf(args, "preferredip=%s", preferredip) ==1) {} else { dc_log(0, "unexpected prio_args format"); return 0; } // check if ip is not too short if (strlen(preferredip) <= 7) { dc_log(0, "prio args: preferredip too short"); return 0; } // Phase 2 : find device in /dev/disk/by-path to match device/ip DIR *dir_p; struct dirent *dir_entry_p; enum { BUFFERSIZE = 1024 }; char buffer[BUFFERSIZE]; char fullpath[BUFFERSIZE] = "/dev/disk/by-path/"; dir_p = opendir(fullpath); // loop to find device in /dev/disk/by-path while( NULL != (dir_entry_p = readdir(dir_p))) { if (dir_entry_p->d_name[0] != '.') { char path[BUFFERSIZE] = "/dev/disk/by-path/"; strcat(path,dir_entry_p->d_name); ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1); if (nchars != -1) { char *device; buffer[nchars] = '\0'; device = find_regex(buffer,"(sd[a-z]+)"); // if device parsed is the right one if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) { char *ip; ip = find_regex(dir_entry_p->d_name,"([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); // if prefferedip and ip fetched matches if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) { // high prio free(ip); free(device); closedir(dir_p); return 20; } free(ip); } free(device); } else { printf("error\n"); } } } // nothing found, low prio closedir(dir_p); return 10; } int getprio(struct path * pp, char * args) { return iet_prio(pp->dev, args); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/ontap.c000066400000000000000000000140111260771327300252760ustar00rootroot00000000000000/* * Copyright 2005 Network Appliance, Inc., All Rights Reserved * Author: David Wysochanski available at davidw@netapp.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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 v2 for more details. */ #include #include #include #include #include #include #include #include #include #include #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define DEFAULT_PRIOVAL 10 #define RESULTS_MAX 256 #define SG_TIMEOUT 60000 #define pp_ontap_log(prio, fmt, args...) \ condlog(prio, "%s: ontap prio: " fmt, dev, ##args) static void dump_cdb(unsigned char *cdb, int size) { int i; char buf[10*5+1]; char * p = &buf[0]; condlog(0, "- SCSI CDB: "); for (i=0; imasked_status, io_hdr->host_status, io_hdr->driver_status); if (io_hdr->sb_len_wr > 0) { condlog(0, "- SCSI sense data: "); for (i=0; isb_len_wr; i++) { p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ", io_hdr->sbp[i]); } condlog(0, "%s", buf); } } /* * Returns: * -1: error, errno set * 0: success */ static int send_gva(const char *dev, int fd, unsigned char pg, unsigned char *results, int *results_size) { unsigned char sb[128]; unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa, pg, sizeof(sb), 0, 0}; struct sg_io_hdr io_hdr; int ret = -1; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(results, 0, *results_size); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (cdb); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = *results_size; io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno); dump_cdb(cdb, sizeof(cdb)); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_ontap_log(0, "SCSI error"); dump_cdb(cdb, sizeof(cdb)); process_sg_error(&io_hdr); goto out; } if (results[4] != 0x0a || results[5] != 0x98 || results[6] != 0x0a ||results[7] != 0x01) { dump_cdb(cdb, sizeof(cdb)); pp_ontap_log(0, "GVA return wrong format "); pp_ontap_log(0, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x", results[4], results[5], results[6], results[7]); goto out; } ret = 0; out: return(ret); } /* * Retuns: * -1: Unable to obtain proxy info * 0: Device _not_ proxy path * 1: Device _is_ proxy path */ static int get_proxy(const char *dev, int fd) { unsigned char results[256]; unsigned char sb[128]; unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0, sizeof(sb), 0}; struct sg_io_hdr io_hdr; int ret = -1; memset(&results, 0, sizeof (results)); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (cdb); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (results); io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "ioctl sending inquiry command failed, " "errno=%d", errno); dump_cdb(cdb, sizeof(cdb)); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_ontap_log(0, "SCSI error"); dump_cdb(cdb, sizeof(cdb)); process_sg_error(&io_hdr); goto out; } if (results[1] != 0xc1 || results[8] != 0x0a || results[9] != 0x98 || results[10] != 0x0a || results[11] != 0x0 || results[12] != 0xc1 || results[13] != 0x0) { pp_ontap_log(0,"proxy info page in unknown format - "); pp_ontap_log(0,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x " "0x%02x 0x%02x", results[8], results[9], results[10], results[11], results[12], results[13]); dump_cdb(cdb, sizeof(cdb)); goto out; } ret = (results[19] & 0x02) >> 1; out: return(ret); } /* * Returns priority of device based on device info. * * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol * 3: iSCSI HBA * 2: iSCSI software * 1: FCP proxy */ static int ontap_prio(const char *dev, int fd) { unsigned char results[RESULTS_MAX]; int results_size=RESULTS_MAX; int rc; int is_proxy; int is_iscsi_software; int is_iscsi_hardware; int tot_len; is_iscsi_software = is_iscsi_hardware = is_proxy = 0; memset(&results, 0, sizeof (results)); rc = send_gva(dev, fd, 0x41, results, &results_size); if (rc >= 0) { tot_len = results[0] << 24 | results[1] << 16 | results[2] << 8 | results[3]; if (tot_len <= 8) { goto try_fcp_proxy; } if (results[8] != 0x41) { pp_ontap_log(0, "GVA page 0x41 error - " "results[8] = 0x%x", results[8]); goto try_fcp_proxy; } if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) || (strncmp((char *)&results[12], "iswt", 4) == 0)) { is_iscsi_software = 1; goto prio_select; } else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) { is_iscsi_hardware = 1; goto prio_select; } } else { return 0; } try_fcp_proxy: rc = get_proxy(dev, fd); if (rc >= 0) { is_proxy = rc; } prio_select: if (is_iscsi_hardware) { return 3; } else if (is_iscsi_software) { return 2; } else { if (is_proxy) { return 1; } else { /* Either non-proxy, or couldn't get proxy info */ return 4; } } } int getprio (struct path * pp, char * args) { return ontap_prio(pp->dev, pp->fd); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/random.c000066400000000000000000000004221260771327300254360ustar00rootroot00000000000000#include #include #include #include #include int getprio (struct path * pp, char * args) { struct timeval tv; gettimeofday(&tv, NULL); srand((unsigned int)tv.tv_usec); return 1+(int) (10.0*rand()/(RAND_MAX+1.0)); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/rdac.c000066400000000000000000000043711260771327300250760ustar00rootroot00000000000000#include #include #include #include #include #include #include #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define pp_rdac_log(prio, msg) condlog(prio, "%s: rdac prio: " msg, dev) int rdac_prio(const char *dev, int fd) { unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = 0; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_rdac_log(0, "sending inquiry command failed"); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_rdac_log(0, "inquiry command indicates error"); goto out; } if (/* Verify the code page - right page & page identifier */ sense_buffer[1] != 0xc9 || sense_buffer[3] != 0x2c || sense_buffer[4] != 'v' || sense_buffer[5] != 'a' || sense_buffer[6] != 'c' ) { pp_rdac_log(0, "volume access control page in unknown format"); goto out; } if ( /* Current Volume Path Bit */ ( sense_buffer[8] & 0x01) == 0x01 ) { /* * This volume was owned by the controller receiving * the inquiry command. */ ret |= 0x02; } /* Volume Preferred Path Priority */ switch ( sense_buffer[9] & 0x0F ) { case 0x01: /* * Access to this volume is most preferred through * this path and other paths with this value. */ ret |= 0x04; break; case 0x02: /* * Access to this volume through this path is to be used * as a secondary path. Typically this path would be used * for fail-over situations. */ ret |= 0x01; break; default: /* Reserved values */ break; } /* For ioship mode set the bit 3 (00001000) */ if ((sense_buffer[8] >> 5) & 0x01) ret |= 0x08; out: return(ret); } int getprio (struct path * pp, char * args) { return rdac_prio(pp->dev, pp->fd); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/weightedpath.c000066400000000000000000000046331260771327300266430ustar00rootroot00000000000000/* * * (C) Copyright 2008 Hewlett-Packard Development Company, L.P * * This file is released under the GPL */ /* * Prioritizer for device mapper multipath, where specific paths and the * corresponding priority values are provided as arguments. * * This prioritizer assigns the priority value provided in the configuration * file based on the comparison made between the specified paths and the path * instance for which this is called. * Paths can be specified as a regular expression of devname of the path or * as hbtl information of the path. * * Examples: * prio "weightedpath hbtl 1:.:.:. 2 4:.:.:. 4" * prio "weightedpath devname sda 10 sde 20" * * Returns zero as the default priority. */ #include #include #include #include "weightedpath.h" #include #include #include #include #include char *get_next_string(char **temp, char *split_char) { char *token = NULL; token = strsep(temp, split_char); while (token != NULL && !strcmp(token, "")) token = strsep(temp, split_char); return token; } /* main priority routine */ int prio_path_weight(struct path *pp, char *prio_args) { char path[FILE_NAME_SIZE]; char *arg; char *temp, *regex, *prio; char split_char[] = " \t"; int priority = DEFAULT_PRIORITY, path_found = 0; regex_t pathe; /* Return default priority if there is no argument */ if (!prio_args) return priority; arg = temp = STRDUP(prio_args); regex = get_next_string(&temp, split_char); /* Return default priority if the argument is not parseable */ if (!regex) { FREE(arg); return priority; } if (!strcmp(regex, HBTL)) { sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } else if (!strcmp(regex, DEV_NAME)) { strcpy(path, pp->dev); } else { condlog(0, "%s: %s - Invalid arguments", pp->dev, pp->prio.name); FREE(arg); return priority; } while (!path_found) { if (!temp) break; if (!(regex = get_next_string(&temp, split_char))) break; if (!(prio = get_next_string(&temp, split_char))) break; if (!regcomp(&pathe, regex, REG_EXTENDED|REG_NOSUB)) { if (!regexec(&pathe, path, 0, NULL, 0)) { path_found = 1; priority = atoi(prio); } regfree(&pathe); } } FREE(arg); return priority; } int getprio(struct path *pp, char *args) { return prio_path_weight(pp, args); } multipath-tools-0.5.0+git1.656f8865/libmultipath/prioritizers/weightedpath.h000066400000000000000000000003501260771327300266400ustar00rootroot00000000000000#ifndef _WEIGHTED_PATH_H #define _WEIGHTED_PATH_H #define PRIO_WEIGHTED_PATH "weightedpath" #define HBTL "hbtl" #define DEV_NAME "devname" #define DEFAULT_PRIORITY 0 int prio_path_weight(struct path *pp, char *prio_args); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/propsel.c000066400000000000000000000365451260771327300231140ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "structs.h" #include "config.h" #include "debug.h" #include "pgpolicies.h" #include "alias.h" #include "defaults.h" #include "devmapper.h" #include "prio.h" #include "discovery.h" #include "dict.h" #include "prioritizers/alua_rtpg.h" #include pgpolicyfn *pgpolicies[] = { NULL, one_path_per_group, one_group, group_by_serial, group_by_prio, group_by_node_name }; #define do_set(var, src, dest, msg) \ do { \ if (src && src->var) { \ dest = src->var; \ origin = msg; \ goto out; \ } \ } while(0) #define do_default(dest, value) \ do { \ dest = value; \ origin = "(internal default)"; \ } while(0) #define mp_set_mpe(var) \ do_set(var, mp->mpe, mp->var, "(LUN setting)") #define mp_set_hwe(var) \ do_set(var, mp->hwe, mp->var, "(controller setting)") #define mp_set_ovr(var) \ do_set(var, conf->overrides, mp->var, "(overrides setting)") #define mp_set_conf(var) \ do_set(var, conf, mp->var, "(config file default)") #define mp_set_default(var, value) \ do_default(mp->var, value) #define pp_set_mpe(var) \ do_set(var, mpe, pp->var, "(LUN setting)") #define pp_set_hwe(var) \ do_set(var, pp->hwe, pp->var, "(controller setting)") #define pp_set_conf(var) \ do_set(var, conf, pp->var, "(config file default)") #define pp_set_ovr(var) \ do_set(var, conf->overrides, pp->var, "(overrides setting)") #define pp_set_default(var, value) \ do_default(pp->var, value) #define do_attr_set(var, src, shift, msg) \ do { \ if (src && (src->attribute_flags & (1 << shift))) { \ mp->attribute_flags |= (1 << shift); \ mp->var = src->var; \ origin = msg; \ goto out; \ } \ } while(0) #define set_attr_mpe(var, shift) \ do_attr_set(var, mp->mpe, shift, "(LUN setting)") #define set_attr_conf(var, shift) \ do_attr_set(var, conf, shift, "(config file default)") extern int select_mode (struct multipath *mp) { char *origin; set_attr_mpe(mode, ATTR_MODE); set_attr_conf(mode, ATTR_MODE); mp->attribute_flags &= ~(1 << ATTR_MODE); return 0; out: condlog(3, "%s: mode = 0%o %s", mp->alias, mp->mode, origin); return 0; } extern int select_uid (struct multipath *mp) { char *origin; set_attr_mpe(uid, ATTR_UID); set_attr_conf(uid, ATTR_UID); mp->attribute_flags &= ~(1 << ATTR_UID); return 0; out: condlog(3, "%s: uid = 0%o %s", mp->alias, mp->uid, origin); return 0; } extern int select_gid (struct multipath *mp) { char *origin; set_attr_mpe(gid, ATTR_GID); set_attr_conf(gid, ATTR_GID); mp->attribute_flags &= ~(1 << ATTR_GID); return 0; out: condlog(3, "%s: gid = 0%o %s", mp->alias, mp->gid, origin); return 0; } /* * selectors : * traverse the configuration layers from most specific to most generic * stop at first explicit setting found */ extern int select_rr_weight (struct multipath * mp) { char *origin, buff[13]; mp_set_mpe(rr_weight); mp_set_ovr(rr_weight); mp_set_hwe(rr_weight); mp_set_conf(rr_weight); mp_set_default(rr_weight, RR_WEIGHT_NONE); out: print_rr_weight(buff, 13, &mp->rr_weight); condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin); return 0; } extern int select_pgfailback (struct multipath * mp) { char *origin, buff[13]; mp_set_mpe(pgfailback); mp_set_ovr(pgfailback); mp_set_hwe(pgfailback); mp_set_conf(pgfailback); mp_set_default(pgfailback, DEFAULT_FAILBACK); out: print_pgfailback(buff, 13, &mp->pgfailback); condlog(3, "%s: failback = %s %s", mp->alias, buff, origin); return 0; } extern int select_pgpolicy (struct multipath * mp) { char *origin, buff[POLICY_NAME_SIZE]; if (conf->pgpolicy_flag > 0) { mp->pgpolicy = conf->pgpolicy_flag; origin = "(cmd line flag)"; goto out; } mp_set_mpe(pgpolicy); mp_set_ovr(pgpolicy); mp_set_hwe(pgpolicy); mp_set_conf(pgpolicy); mp_set_default(pgpolicy, DEFAULT_PGPOLICY); out: mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(buff, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: path_grouping_policy = %s %s", mp->alias, buff, origin); return 0; } extern int select_selector (struct multipath * mp) { char *origin; mp_set_mpe(selector); mp_set_ovr(selector); mp_set_hwe(selector); mp_set_conf(selector); mp_set_default(selector, DEFAULT_SELECTOR); out: mp->selector = STRDUP(mp->selector); condlog(3, "%s: path_selector = \"%s\" %s", mp->alias, mp->selector, origin); return 0; } static void select_alias_prefix (struct multipath * mp) { char *origin; mp_set_ovr(alias_prefix); mp_set_hwe(alias_prefix); mp_set_conf(alias_prefix); mp_set_default(alias_prefix, DEFAULT_ALIAS_PREFIX); out: condlog(3, "%s: alias_prefix = %s %s", mp->wwid, mp->alias_prefix, origin); } static int want_user_friendly_names(struct multipath * mp) { char *origin; int user_friendly_names; do_set(user_friendly_names, mp->mpe, user_friendly_names, "(LUN setting)"); do_set(user_friendly_names, conf->overrides, user_friendly_names, "(overrides setting)"); do_set(user_friendly_names, mp->hwe, user_friendly_names, "(controller setting)"); do_set(user_friendly_names, conf, user_friendly_names, "(config file setting)"); do_default(user_friendly_names, USER_FRIENDLY_NAMES_OFF); out: condlog(3, "%s: user_friendly_names = %s %s", mp->wwid, (user_friendly_names == USER_FRIENDLY_NAMES_ON)? "yes" : "no", origin); return (user_friendly_names == USER_FRIENDLY_NAMES_ON); } extern int select_alias (struct multipath * mp) { char *origin = NULL; if (mp->mpe && mp->mpe->alias) { mp->alias = STRDUP(mp->mpe->alias); origin = "(LUN setting)"; goto out; } mp->alias = NULL; if (!want_user_friendly_names(mp)) goto out; select_alias_prefix(mp); if (strlen(mp->alias_old) > 0) { mp->alias = use_existing_alias(mp->wwid, conf->bindings_file, mp->alias_old, mp->alias_prefix, conf->bindings_read_only); memset (mp->alias_old, 0, WWID_SIZE); origin = "(using existing alias)"; } if (mp->alias == NULL) { mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file, mp->alias_prefix, conf->bindings_read_only); origin = "(user_friendly_name)"; } out: if (mp->alias == NULL) { mp->alias = STRDUP(mp->wwid); origin = "(default to wwid)"; } if (mp->alias) condlog(3, "%s: alias = %s %s", mp->wwid, mp->alias, origin); return mp->alias ? 0 : 1; } extern int select_features (struct multipath * mp) { char *origin; mp_set_mpe(features); mp_set_ovr(features); mp_set_hwe(features); mp_set_conf(features); mp_set_default(features, DEFAULT_FEATURES); out: mp->features = STRDUP(mp->features); condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin); if (strstr(mp->features, "queue_if_no_path")) { if (mp->no_path_retry == NO_PATH_RETRY_UNDEF) mp->no_path_retry = NO_PATH_RETRY_QUEUE; else if (mp->no_path_retry == NO_PATH_RETRY_FAIL) { condlog(1, "%s: config error, overriding 'no_path_retry' value", mp->alias); mp->no_path_retry = NO_PATH_RETRY_QUEUE; } } return 0; } extern int select_hwhandler (struct multipath * mp) { char *origin; mp_set_hwe(hwhandler); mp_set_conf(hwhandler); mp_set_default(hwhandler, DEFAULT_HWHANDLER); out: mp->hwhandler = STRDUP(mp->hwhandler); condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, origin); return 0; } extern int select_checker(struct path *pp) { char *origin, *checker_name; struct checker * c = &pp->checker; do_set(checker_name, conf->overrides, checker_name, "(overrides setting)"); do_set(checker_name, pp->hwe, checker_name, "(controller setting)"); do_set(checker_name, conf, checker_name, "(config file setting)"); do_default(checker_name, DEFAULT_CHECKER); out: checker_get(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 (config file default)", pp->dev, c->timeout); } else if (sysfs_get_timeout(pp, &c->timeout) > 0) condlog(3, "%s: checker timeout = %u ms (sysfs setting)", pp->dev, c->timeout); else { c->timeout = DEF_TIMEOUT; condlog(3, "%s: checker timeout = %u ms (internal default)", pp->dev, c->timeout); } return 0; } extern int select_getuid (struct path * pp) { char *origin; pp_set_ovr(getuid); pp_set_ovr(uid_attribute); pp_set_hwe(getuid); pp_set_hwe(uid_attribute); pp_set_conf(getuid); pp_set_conf(uid_attribute); pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE); out: if (pp->uid_attribute) condlog(3, "%s: uid_attribute = %s %s", pp->dev, pp->uid_attribute, origin); else if (pp->getuid) condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid, origin); return 0; } void detect_prio(struct path * pp) { int ret; struct prio *p = &pp->prio; if (get_target_port_group_support(pp->fd) <= 0) return; ret = get_target_port_group(pp->fd); if (ret < 0) return; if (get_asymmetric_access_state(pp->fd, ret) < 0) return; prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS); } #define set_prio(src, msg) \ do { \ if (src && src->prio_name) { \ prio_get(p, src->prio_name, src->prio_args); \ origin = msg; \ goto out; \ } \ } while(0) extern int select_prio (struct path * pp) { char *origin; struct mpentry * mpe; struct prio * p = &pp->prio; if (pp->detect_prio == DETECT_PRIO_ON) { detect_prio(pp); if (prio_selected(p)) { origin = "(detected setting)"; goto out; } } mpe = find_mpe(pp->wwid); set_prio(mpe, "(LUN setting)"); set_prio(conf->overrides, "(overrides setting)"); set_prio(pp->hwe, "controller setting)"); set_prio(conf, "(config file default)"); prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS); origin = "(internal default)"; out: condlog(3, "%s: prio = %s %s", pp->dev, prio_name(p), origin); condlog(3, "%s: prio args = \"%s\" %s", pp->dev, prio_args(p), origin); return 0; } extern int select_no_path_retry(struct multipath *mp) { char *origin = NULL; char buff[12]; if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) { condlog(0, "flush_on_last_del in progress"); mp->no_path_retry = NO_PATH_RETRY_FAIL; return 0; } mp_set_mpe(no_path_retry); mp_set_ovr(no_path_retry); mp_set_hwe(no_path_retry); mp_set_conf(no_path_retry); out: 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 if (mp->no_path_retry != NO_PATH_RETRY_UNDEF) condlog(3, "%s: no_path_retry = %s (inheritied setting)", mp->alias, buff); else condlog(3, "%s: no_path_retry = undef (internal default)", mp->alias); return 0; } int select_minio_rq (struct multipath * mp) { char *origin; do_set(minio_rq, mp->mpe, mp->minio, "(LUN setting)"); do_set(minio_rq, conf->overrides, mp->minio, "(overrides setting)"); do_set(minio_rq, mp->hwe, mp->minio, "(controller setting)"); do_set(minio_rq, conf, mp->minio, "(config file setting)"); do_default(mp->minio, DEFAULT_MINIO_RQ); out: condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } int select_minio_bio (struct multipath * mp) { char *origin; mp_set_mpe(minio); mp_set_ovr(minio); mp_set_hwe(minio); mp_set_conf(minio); mp_set_default(minio, DEFAULT_MINIO); out: condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } extern int select_minio (struct multipath * mp) { unsigned int minv_dmrq[3] = {1, 1, 0}; if (VERSION_GE(conf->version, minv_dmrq)) return select_minio_rq(mp); else return select_minio_bio(mp); } extern int select_fast_io_fail(struct multipath *mp) { char *origin, 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); condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); return 0; } extern int select_dev_loss(struct multipath *mp) { char *origin, buff[12]; mp_set_ovr(dev_loss); mp_set_hwe(dev_loss); mp_set_conf(dev_loss); mp->dev_loss = 0; return 0; out: print_dev_loss(buff, 12, &mp->dev_loss); condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin); return 0; } extern int select_flush_on_last_del(struct multipath *mp) { char *origin; if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) return 0; mp_set_mpe(flush_on_last_del); mp_set_ovr(flush_on_last_del); mp_set_hwe(flush_on_last_del); mp_set_conf(flush_on_last_del); mp_set_default(flush_on_last_del, FLUSH_DISABLED); out: condlog(3, "%s: flush_on_last_del = %s %s", mp->alias, (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin); return 0; } extern int select_reservation_key (struct multipath * mp) { char *origin, buff[12]; mp_set_mpe(reservation_key); mp_set_conf(reservation_key); mp->reservation_key = NULL; return 0; out: print_reservation_key(buff, 12, &mp->reservation_key); condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin); return 0; } extern int select_retain_hwhandler (struct multipath * mp) { char *origin; unsigned int minv_dm_retain[3] = {1, 5, 0}; if (!VERSION_GE(conf->version, minv_dm_retain)) { mp->retain_hwhandler = RETAIN_HWHANDLER_OFF; origin = "(requires kernel version >= 1.5.0)"; goto out; } mp_set_ovr(retain_hwhandler); mp_set_hwe(retain_hwhandler); mp_set_conf(retain_hwhandler); mp_set_default(retain_hwhandler, DEFAULT_RETAIN_HWHANDLER); out: condlog(3, "%s: retain_attached_hw_handler = %s %s", mp->alias, (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)? "yes" : "no", origin); return 0; } extern int select_detect_prio (struct path * pp) { char *origin; pp_set_ovr(detect_prio); pp_set_hwe(detect_prio); pp_set_conf(detect_prio); pp_set_default(detect_prio, DEFAULT_DETECT_PRIO); out: condlog(3, "%s: detect_prio = %s %s", pp->dev, (pp->detect_prio == DETECT_PRIO_ON)? "yes" : "no", origin); return 0; } extern int select_deferred_remove (struct multipath *mp) { char *origin; #ifndef LIBDM_API_DEFERRED mp->deferred_remove = DEFERRED_REMOVE_OFF; origin = "(not compiled with support)"; goto out; #endif if (mp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) { condlog(3, "%s: deferred remove in progress", mp->alias); return 0; } mp_set_mpe(deferred_remove); mp_set_ovr(deferred_remove); mp_set_hwe(deferred_remove); mp_set_conf(deferred_remove); mp_set_default(deferred_remove, DEFAULT_DEFERRED_REMOVE); out: condlog(3, "%s: deferred_remove = %s %s", mp->alias, (mp->deferred_remove == DEFERRED_REMOVE_ON)? "yes" : "no", origin); return 0; } extern int select_delay_watch_checks(struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(delay_watch_checks); mp_set_ovr(delay_watch_checks); mp_set_hwe(delay_watch_checks); mp_set_conf(delay_watch_checks); mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS); out: print_delay_checks(buff, 12, &mp->delay_watch_checks); condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin); return 0; } extern int select_delay_wait_checks(struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(delay_wait_checks); mp_set_ovr(delay_wait_checks); mp_set_hwe(delay_wait_checks); mp_set_conf(delay_wait_checks); mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS); out: print_delay_checks(buff, 12, &mp->delay_wait_checks); condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin); return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/propsel.h000066400000000000000000000020641260771327300231060ustar00rootroot00000000000000int select_rr_weight (struct multipath * mp); int select_pgfailback (struct multipath * mp); int select_pgpolicy (struct multipath * mp); int select_selector (struct multipath * mp); int select_alias (struct multipath * mp); int select_features (struct multipath * mp); int select_hwhandler (struct multipath * mp); int select_checker(struct path *pp); int select_getuid (struct path * pp); int select_prio (struct path * pp); int select_no_path_retry(struct multipath *mp); int select_flush_on_last_del(struct multipath *mp); int select_minio(struct multipath *mp); int select_mode(struct multipath *mp); int select_uid(struct multipath *mp); int select_gid(struct multipath *mp); int select_fast_io_fail(struct multipath *mp); int select_dev_loss(struct multipath *mp); int select_reservation_key(struct multipath *mp); int select_retain_hwhandler (struct multipath * mp); int select_detect_prio(struct path * pp); int select_deferred_remove(struct multipath *mp); int select_delay_watch_checks (struct multipath * mp); int select_delay_wait_checks (struct multipath * mp); multipath-tools-0.5.0+git1.656f8865/libmultipath/sg_include.h000066400000000000000000000026341260771327300235410ustar00rootroot00000000000000#define __user #include #ifndef DID_OK #define DID_OK 0x00 /* NO error */ #define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */ #define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */ #define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */ #define DID_BAD_TARGET 0x04 /* BAD target. */ #define DID_ABORT 0x05 /* Told to abort for some other reason */ #define DID_PARITY 0x06 /* Parity error */ #define DID_ERROR 0x07 /* Internal error */ #define DID_RESET 0x08 /* Reset by somebody. */ #define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */ #define DID_PASSTHROUGH 0x0a /* Force command past mid-layer */ #define DID_SOFT_ERROR 0x0b /* The low level driver just wish a retry */ #define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */ #define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also * without decrementing the retry count */ #define DID_TRANSPORT_DISRUPTED 0x0e /* Transport error disrupted execution * and the driver blocked the port to * recover the link. Transport class will * retry or fail IO */ #define DID_TRANSPORT_FAILFAST 0x0f /* Transport class fastfailed the io */ #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/structs.c000066400000000000000000000240771260771327300231340ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2004 Stefan Bader, IBM */ #include #include #include #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "util.h" #include "structs.h" #include "config.h" #include "debug.h" #include "structs_vec.h" #include "blacklist.h" #include "prio.h" struct adapter_group * alloc_adaptergroup(void) { struct adapter_group *agp; agp = (struct adapter_group *)MALLOC(sizeof(struct adapter_group)); if (!agp) return NULL; agp->host_groups = vector_alloc(); if (!agp->host_groups) { FREE(agp); agp = NULL; } return agp; } void free_adaptergroup(vector adapters) { int i; struct adapter_group *agp; vector_foreach_slot(adapters, agp, i) { free_hostgroup(agp->host_groups); FREE(agp); } vector_free(adapters); } void free_hostgroup(vector hostgroups) { int i; struct host_group *hgp; if (!hostgroups) return; vector_foreach_slot(hostgroups, hgp, i) { vector_free(hgp->paths); FREE(hgp); } vector_free(hostgroups); } struct host_group * alloc_hostgroup(void) { struct host_group *hgp; hgp = (struct host_group *)MALLOC(sizeof(struct host_group)); if (!hgp) return NULL; hgp->paths = vector_alloc(); if (!hgp->paths) { FREE(hgp); hgp = NULL; } return hgp; } struct path * alloc_path (void) { struct path * pp; pp = (struct path *)MALLOC(sizeof(struct path)); if (pp) { pp->sg_id.host_no = -1; pp->sg_id.channel = -1; pp->sg_id.scsi_id = -1; pp->sg_id.lun = -1; pp->fd = -1; pp->priority = PRIO_UNDEF; } return pp; } void free_path (struct path * pp) { if (!pp) return; if (checker_selected(&pp->checker)) checker_put(&pp->checker); if (prio_selected(&pp->prio)) prio_put(&pp->prio); if (pp->fd >= 0) close(pp->fd); if (pp->udev) { udev_device_unref(pp->udev); pp->udev = NULL; } FREE(pp); } void free_pathvec (vector vec, enum free_path_mode free_paths) { int i; struct path * pp; if (!vec) return; if (free_paths == FREE_PATHS) vector_foreach_slot(vec, pp, i) free_path(pp); vector_free(vec); } struct pathgroup * alloc_pathgroup (void) { struct pathgroup * pgp; pgp = (struct pathgroup *)MALLOC(sizeof(struct pathgroup)); if (!pgp) return NULL; pgp->paths = vector_alloc(); if (!pgp->paths) { FREE(pgp); pgp = NULL; } return pgp; } void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths) { if (!pgp) return; free_pathvec(pgp->paths, free_paths); FREE(pgp); } void free_pgvec (vector pgvec, enum free_path_mode free_paths) { int i; struct pathgroup * pgp; if (!pgvec) return; vector_foreach_slot(pgvec, pgp, i) free_pathgroup(pgp, free_paths); vector_free(pgvec); } struct multipath * alloc_multipath (void) { struct multipath * mpp; mpp = (struct multipath *)MALLOC(sizeof(struct multipath)); if (mpp) { mpp->bestpg = 1; mpp->mpcontext = NULL; mpp->no_path_retry = NO_PATH_RETRY_UNDEF; mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET; } return mpp; } extern void free_multipath_attributes (struct multipath * mpp) { if (!mpp) return; if (mpp->selector) { FREE(mpp->selector); mpp->selector = NULL; } if (mpp->features) { FREE(mpp->features); mpp->features = NULL; } if (mpp->hwhandler) { FREE(mpp->hwhandler); mpp->hwhandler = NULL; } } void free_multipath (struct multipath * mpp, enum free_path_mode free_paths) { if (!mpp) return; free_multipath_attributes(mpp); if (mpp->alias) { FREE(mpp->alias); mpp->alias = NULL; } if (mpp->dmi) { FREE(mpp->dmi); mpp->dmi = NULL; } free_pathvec(mpp->paths, free_paths); free_pgvec(mpp->pg, free_paths); FREE_PTR(mpp->mpcontext); FREE(mpp); } void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths) { int i; struct multipath * mpp; if (!mpvec) return; vector_foreach_slot (mpvec, mpp, i) { if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) { free_multipath(mpp, free_paths); vector_del_slot(mpvec, i); return; } } } void free_multipathvec (vector mpvec, enum free_path_mode free_paths) { int i; struct multipath * mpp; if (!mpvec) return; vector_foreach_slot (mpvec, mpp, i) free_multipath(mpp, free_paths); vector_free(mpvec); } int store_path (vector pathvec, struct path * pp) { if (!vector_alloc_slot(pathvec)) return 1; vector_set_slot(pathvec, pp); return 0; } int store_pathgroup (vector pgvec, struct pathgroup * pgp) { if (!vector_alloc_slot(pgvec)) return 1; vector_set_slot(pgvec, pgp); return 0; } int store_hostgroup(vector hostgroupvec, struct host_group * hgp) { if (!vector_alloc_slot(hostgroupvec)) return 1; vector_set_slot(hostgroupvec, hgp); return 0; } int store_adaptergroup(vector adapters, struct adapter_group * agp) { if (!vector_alloc_slot(adapters)) return 1; vector_set_slot(adapters, agp); return 0; } struct multipath * find_mp_by_minor (vector mpvec, int minor) { int i; struct multipath * mpp; if (!mpvec) return NULL; vector_foreach_slot (mpvec, mpp, i) { if (!mpp->dmi) continue; if (mpp->dmi->minor == minor) return mpp; } return NULL; } struct multipath * find_mp_by_wwid (vector mpvec, char * wwid) { int i; struct multipath * mpp; if (!mpvec) return NULL; vector_foreach_slot (mpvec, mpp, i) if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) return mpp; return NULL; } struct multipath * find_mp_by_alias (vector mpvec, char * alias) { int i; int len; struct multipath * mpp; if (!mpvec) return NULL; len = strlen(alias); if (!len) return NULL; vector_foreach_slot (mpvec, mpp, i) { if (strlen(mpp->alias) == len && !strncmp(mpp->alias, alias, len)) return mpp; } return NULL; } struct multipath * find_mp_by_str (vector mpvec, char * str) { int minor; if (sscanf(str, "dm-%d", &minor) == 1) return find_mp_by_minor(mpvec, minor); else return find_mp_by_alias(mpvec, str); } struct path * find_path_by_dev (vector pathvec, char * dev) { int i; struct path * pp; if (!pathvec) return NULL; vector_foreach_slot (pathvec, pp, i) if (!strcmp(pp->dev, dev)) return pp; condlog(3, "%s: not found in pathvec", dev); return NULL; } struct path * find_path_by_devt (vector pathvec, char * dev_t) { int i; struct path * pp; if (!pathvec) return NULL; vector_foreach_slot (pathvec, pp, i) if (!strcmp(pp->dev_t, dev_t)) return pp; condlog(3, "%s: not found in pathvec", dev_t); return NULL; } extern int pathcountgr (struct pathgroup * pgp, int state) { struct path *pp; int count = 0; int i; vector_foreach_slot (pgp->paths, pp, i) if ((pp->state == state) || (state == PATH_WILD)) count++; return count; } extern int pathcount (struct multipath * mpp, int state) { struct pathgroup *pgp; int count = 0; int i; if (mpp->pg) { vector_foreach_slot (mpp->pg, pgp, i) count += pathcountgr(pgp, state); } return count; } extern int pathcmp (struct pathgroup *pgp, struct pathgroup *cpgp) { int i, j; struct path *pp, *cpp; int pnum = 0, found = 0; vector_foreach_slot(pgp->paths, pp, i) { pnum++; vector_foreach_slot(cpgp->paths, cpp, j) { if ((long)pp == (long)cpp) { found++; break; } } } return pnum - found; } struct path * first_path (struct multipath * mpp) { struct pathgroup * pgp; if (!mpp->pg) return NULL; pgp = VECTOR_SLOT(mpp->pg, 0); return pgp?VECTOR_SLOT(pgp->paths, 0):NULL; } extern void setup_feature(struct multipath * mpp, char *feature) { if (!strncmp(feature, "queue_if_no_path", 16)) { if (mpp->no_path_retry <= NO_PATH_RETRY_UNDEF) mpp->no_path_retry = NO_PATH_RETRY_QUEUE; } } extern int add_feature (char **f, char *n) { int c = 0, d, l; char *e, *p, *t; if (!f) return 1; /* Nothing to do */ if (!n || *n == '0') return 0; /* Check if feature is already present */ if (strstr(*f, n)) return 0; /* Get feature count */ c = strtoul(*f, &e, 10); if (*f == e) /* parse error */ return 1; /* Check if we need to increase feature count space */ l = strlen(*f) + strlen(n) + 1; /* Count new features */ if ((c % 10) == 9) l++; c++; p = n; while (*p != '\0') { if (*p == ' ' && p[1] != '\0' && p[1] != ' ') { if ((c % 10) == 9) l++; c++; } p++; } t = MALLOC(l + 1); if (!t) return 1; memset(t, 0, l + 1); /* Update feature count */ d = c; l = 1; while (d > 9) { d /= 10; l++; } p = t; snprintf(p, l + 2, "%0d ", c); /* Copy the feature string */ p = strchr(*f, ' '); if (p) { while (*p == ' ') p++; strcat(t, p); strcat(t, " "); } else { p = t + strlen(t); } strcat(t, n); FREE(*f); *f = t; return 0; } extern int remove_feature(char **f, char *o) { int c = 0, d, l; char *e, *p, *n; if (!f || !*f) return 1; /* Nothing to do */ if (!o || *o == '\0') return 0; /* Check if not present */ if (!strstr(*f, o)) return 0; /* Get feature count */ c = strtoul(*f, &e, 10); if (*f == e) /* parse error */ return 1; /* Normalize features */ while (*o == ' ') { o++; } /* Just spaces, return */ if (*o == '\0') return 0; e = o + strlen(o); while (*e == ' ') e--; d = (int)(e - o); /* Update feature count */ c--; p = o; while (p[0] != '\0') { if (p[0] == ' ' && p[1] != ' ' && p[1] != '\0') c--; p++; } /* Quick exit if all features have been removed */ if (c == 0) { n = MALLOC(2); if (!n) return 1; strcpy(n, "0"); goto out; } /* Search feature to be removed */ e = strstr(*f, o); if (!e) /* Not found, return */ return 0; /* Update feature count space */ l = strlen(*f) - d; n = MALLOC(l + 1); if (!n) return 1; /* Copy the feature count */ sprintf(n, "%0d", c); /* * Copy existing features up to the feature * about to be removed */ p = strchr(*f, ' '); if (!p) /* Internal error, feature string inconsistent */ return 1; while (*p == ' ') p++; p--; if (e != p) { do { e--; d++; } while (*e == ' '); e++; d--; strncat(n, p, (size_t)(e - p)); p += (size_t)(e - p); } /* Skip feature to be removed */ p += d; /* Copy remaining features */ if (strlen(p)) { while (*p == ' ') p++; if (strlen(p)) { p--; strcat(n, p); } } out: FREE(*f); *f = n; return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/structs.h000066400000000000000000000161121260771327300231300ustar00rootroot00000000000000#ifndef _STRUCTS_H #define _STRUCTS_H #include #include "prio.h" #define WWID_SIZE 128 #define SERIAL_SIZE 65 #define NODE_NAME_SIZE 224 #define PATH_STR_SIZE 16 #define PARAMS_SIZE 4096 #define FILE_NAME_SIZE 256 #define CALLOUT_MAX_SIZE 256 #define BLK_DEV_SIZE 33 #define PATH_SIZE 512 #define NAME_SIZE 512 #define HOST_NAME_LEN 16 #define SLOT_NAME_SIZE 40 #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 #define SCSI_REV_SIZE 5 #define SCSI_STATE_SIZE 19 #define NO_PATH_RETRY_UNDEF 0 #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 enum free_path_mode { KEEP_PATHS, FREE_PATHS }; enum rr_weight_mode { RR_WEIGHT_UNDEF, RR_WEIGHT_NONE, RR_WEIGHT_PRIO }; enum failback_mode { FAILBACK_UNDEF, FAILBACK_MANUAL, FAILBACK_IMMEDIATE, FAILBACK_FOLLOWOVER }; enum sysfs_buses { SYSFS_BUS_UNDEF, SYSFS_BUS_SCSI, SYSFS_BUS_IDE, SYSFS_BUS_CCW, SYSFS_BUS_CCISS, }; enum pathstates { PSTATE_UNDEF, PSTATE_FAILED, PSTATE_ACTIVE }; enum pgstates { PGSTATE_UNDEF, PGSTATE_ENABLED, PGSTATE_DISABLED, PGSTATE_ACTIVE }; enum yes_no_states { YN_NO, YN_YES, }; enum queue_without_daemon_states { QUE_NO_DAEMON_OFF = YN_NO, QUE_NO_DAEMON_ON = YN_YES, QUE_NO_DAEMON_FORCE, }; enum attribute_bits { ATTR_UID, ATTR_GID, ATTR_MODE, }; enum yes_no_undef_states { YNU_UNDEF, YNU_NO, YNU_YES, }; enum flush_states { FLUSH_UNDEF = YNU_UNDEF, FLUSH_DISABLED = YNU_NO, FLUSH_ENABLED = YNU_YES, FLUSH_IN_PROGRESS, }; enum log_checker_err_states { LOG_CHKR_ERR_ALWAYS, LOG_CHKR_ERR_ONCE, }; enum user_friendly_names_states { USER_FRIENDLY_NAMES_UNDEF = YNU_UNDEF, USER_FRIENDLY_NAMES_OFF = YNU_NO, USER_FRIENDLY_NAMES_ON = YNU_YES, }; enum retain_hwhandler_states { RETAIN_HWHANDLER_UNDEF = YNU_UNDEF, RETAIN_HWHANDLER_OFF = YNU_NO, RETAIN_HWHANDLER_ON = YNU_YES, }; enum detect_prio_states { DETECT_PRIO_UNDEF = YNU_UNDEF, DETECT_PRIO_OFF = YNU_NO, DETECT_PRIO_ON = YNU_YES, }; enum deferred_remove_states { DEFERRED_REMOVE_UNDEF = YNU_UNDEF, DEFERRED_REMOVE_OFF = YNU_NO, DEFERRED_REMOVE_ON = YNU_YES, DEFERRED_REMOVE_IN_PROGRESS, }; enum scsi_protocol { SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ SCSI_PROTOCOL_SSA = 2, /* Serial Storage Architecture - Obsolete */ SCSI_PROTOCOL_SBP = 3, /* firewire */ SCSI_PROTOCOL_SRP = 4, /* Infiniband RDMA */ SCSI_PROTOCOL_ISCSI = 5, SCSI_PROTOCOL_SAS = 6, SCSI_PROTOCOL_ADT = 7, /* Media Changers */ SCSI_PROTOCOL_ATA = 8, SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */ }; enum delay_checks_states { DELAY_CHECKS_OFF = -1, DELAY_CHECKS_UNDEF = 0, }; struct sg_id { int host_no; int channel; int scsi_id; int lun; short h_cmd_per_lun; short d_queue_depth; enum scsi_protocol proto_id; int transport_id; }; # ifndef HDIO_GETGEO # define HDIO_GETGEO 0x0301 /* get device geometry */ struct hd_geometry { unsigned char heads; unsigned char sectors; unsigned short cylinders; unsigned long start; }; #endif struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; struct udev_device *udev; struct sg_id sg_id; struct hd_geometry geom; char wwid[WWID_SIZE]; char vendor_id[SCSI_VENDOR_SIZE]; char product_id[SCSI_PRODUCT_SIZE]; char rev[SCSI_REV_SIZE]; char serial[SERIAL_SIZE]; char tgt_node_name[NODE_NAME_SIZE]; unsigned long long size; unsigned int checkint; unsigned int tick; int bus; int offline; int state; int dmstate; int chkrstate; int failcount; int priority; int pgindex; int detect_prio; int watch_checks; int wait_checks; char * uid_attribute; char * getuid; struct prio prio; char * prio_args; struct checker checker; struct multipath * mpp; int fd; int initialized; /* configlet pointers */ struct hwentry * hwe; }; typedef int (pgpolicyfn) (struct multipath *); struct multipath { char wwid[WWID_SIZE]; char alias_old[WWID_SIZE]; int pgpolicy; pgpolicyfn *pgpolicyfn; int nextpg; int bestpg; int queuedio; int action; int pgfailback; int failback_tick; int rr_weight; int nr_active; /* current available(= not known as failed) paths */ int no_path_retry; /* number of retries after all paths are down */ int retry_tick; /* remaining times for retries */ int minio; int flush_on_last_del; int attribute_flags; int fast_io_fail; int retain_hwhandler; int deferred_remove; int delay_watch_checks; int delay_wait_checks; unsigned int dev_loss; uid_t uid; gid_t gid; mode_t mode; unsigned long long size; vector paths; vector pg; struct dm_info * dmi; /* configlet pointers */ char * alias; char * alias_prefix; char * selector; char * features; char * hwhandler; struct mpentry * mpe; struct hwentry * hwe; /* threads */ pthread_t waiter; /* stats */ unsigned int stat_switchgroup; unsigned int stat_path_failures; unsigned int stat_map_loads; unsigned int stat_total_queueing_time; unsigned int stat_queueing_timeouts; /* checkers shared data */ void * mpcontext; /* persistent management data*/ unsigned char * reservation_key; unsigned char prflag; }; struct pathgroup { long id; int status; int priority; int enabled_paths; vector paths; char * selector; }; struct adapter_group { char adapter_name[SLOT_NAME_SIZE]; struct pathgroup *pgp; int num_hosts; vector host_groups; int next_host_index; }; struct host_group { int host_no; int num_paths; vector paths; }; struct path * alloc_path (void); struct pathgroup * alloc_pathgroup (void); struct multipath * alloc_multipath (void); void free_path (struct path *); void free_pathvec (vector vec, enum free_path_mode free_paths); void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths); void free_pgvec (vector pgvec, enum free_path_mode free_paths); void free_multipath (struct multipath *, enum free_path_mode free_paths); void free_multipath_attributes (struct multipath *); void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths); void free_multipathvec (vector mpvec, enum free_path_mode free_paths); struct adapter_group * alloc_adaptergroup(void); struct host_group * alloc_hostgroup(void); void free_adaptergroup(vector adapters); void free_hostgroup(vector hostgroups); int store_adaptergroup(vector adapters, struct adapter_group *agp); int store_hostgroup(vector hostgroupvec, struct host_group *hgp); int store_path (vector pathvec, struct path * pp); int store_pathgroup (vector pgvec, struct pathgroup * pgp); struct multipath * find_mp_by_alias (vector mp, char * alias); struct multipath * find_mp_by_wwid (vector mp, char * wwid); struct multipath * find_mp_by_str (vector mp, char * wwid); struct multipath * find_mp_by_minor (vector mp, int minor); struct path * find_path_by_devt (vector pathvec, char * devt); struct path * find_path_by_dev (vector pathvec, char * dev); struct path * first_path (struct multipath * mpp); int pathcountgr (struct pathgroup *, int); int pathcount (struct multipath *, int); int pathcmp (struct pathgroup *, struct pathgroup *); void setup_feature(struct multipath *, char *); int add_feature (char **, char *); int remove_feature (char **, char *); extern char sysfs_path[PATH_SIZE]; #endif /* _STRUCTS_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/structs_vec.c000066400000000000000000000306271260771327300237670ustar00rootroot00000000000000#include #include #include #include "checkers.h" #include "vector.h" #include "defaults.h" #include "debug.h" #include "structs.h" #include "structs_vec.h" #include "sysfs.h" #include "waiter.h" #include "devmapper.h" #include "dmparser.h" #include "config.h" #include "propsel.h" #include "discovery.h" #include "prio.h" /* * creates or updates mpp->paths reading mpp->pg */ extern int update_mpp_paths(struct multipath * mpp, vector pathvec) { struct pathgroup * pgp; struct path * pp; int i,j; if (!mpp || !mpp->pg) return 0; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (!find_path_by_devt(mpp->paths, pp->dev_t) && (find_path_by_devt(pathvec, pp->dev_t)) && store_path(mpp->paths, pp)) return 1; } } return 0; } extern int adopt_paths (vector pathvec, struct multipath * mpp, int get_info) { int i; struct path * pp; if (!mpp) return 0; if (update_mpp_paths(mpp, pathvec)) return 1; vector_foreach_slot (pathvec, pp, i) { if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) { condlog(3, "%s: ownership set to %s", pp->dev, mpp->alias); pp->mpp = mpp; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; if (!find_path_by_dev(mpp->paths, pp->dev) && store_path(mpp->paths, pp)) return 1; if (get_info && pathinfo(pp, conf->hwtable, DI_PRIO | DI_CHECKER)) return 1; } } return 0; } extern void orphan_path (struct path * pp, const char *reason) { condlog(3, "%s: orphan path, %s", pp->dev, reason); pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->uid_attribute = NULL; pp->getuid = NULL; prio_put(&pp->prio); checker_put(&pp->checker); if (pp->fd >= 0) close(pp->fd); pp->fd = -1; } extern void orphan_paths (vector pathvec, struct multipath * mpp) { int i; struct path * pp; vector_foreach_slot (pathvec, pp, i) { if (pp->mpp == mpp) { orphan_path(pp, "map flushed"); } } } 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) { 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 */ orphan_paths(vecs->pathvec, mpp); if (purge_vec && (i = find_slot(vecs->mpvec, (void *)mpp)) != -1) vector_del_slot(vecs->mpvec, i); /* * final free */ free_multipath(mpp, KEEP_PATHS); } extern void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec) { _remove_map(mpp, vecs, KEEP_WAITER, purge_vec); } extern void remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, int purge_vec) { _remove_map(mpp, vecs, STOP_WAITER, purge_vec); } static void _remove_maps (struct vectors * vecs, int stop_waiter) { int i; struct multipath * mpp; if (!vecs) return; vector_foreach_slot (vecs->mpvec, mpp, i) { _remove_map(mpp, vecs, stop_waiter, 1); i--; } vector_free(vecs->mpvec); vecs->mpvec = NULL; } extern void remove_maps (struct vectors * vecs) { _remove_maps(vecs, KEEP_WAITER); } extern void remove_maps_and_stop_waiters (struct vectors * vecs) { _remove_maps(vecs, STOP_WAITER); } static struct hwentry * extract_hwe_from_path(struct multipath * mpp) { struct path * pp = NULL; int pg_num = -1, p_num = -1, i; struct pathgroup * pgp = NULL; condlog(3, "%s: searching paths for valid hwe", mpp->alias); if (mpp && mpp->pg) { vector_foreach_slot(mpp->pg, pgp, i) { if (pgp->status == PGSTATE_ACTIVE || pgp->status == PGSTATE_ENABLED) { pg_num = i; break; } } if (pg_num >= 0) pgp = VECTOR_SLOT(mpp->pg, pg_num); } if (pgp && pgp->paths) { vector_foreach_slot(pgp->paths, pp, i) { if (pp->dmstate == PSTATE_FAILED) continue; if (strlen(pp->vendor_id) > 0 && strlen(pp->product_id) > 0 && strlen(pp->rev) > 0) { p_num = i; break; } } if (p_num >= 0) pp = VECTOR_SLOT(pgp->paths, i); } if (pp) { if (!strlen(pp->vendor_id) || !strlen(pp->product_id) || !strlen(pp->rev)) { condlog(3, "%s: no device details available", pp->dev); return NULL; } condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: rev = %s", pp->dev, pp->rev); if (!pp->hwe) { condlog(3, "searching hwtable"); pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); } } return pp?pp->hwe:NULL; } static int update_multipath_table (struct multipath *mpp, vector pathvec) { char params[PARAMS_SIZE] = {0}; if (!mpp) return 1; if (dm_get_map(mpp->alias, &mpp->size, params)) { condlog(3, "%s: cannot get map", mpp->alias); return 1; } if (disassemble_map(pathvec, params, mpp)) { condlog(3, "%s: cannot disassemble map", mpp->alias); return 1; } return 0; } static int update_multipath_status (struct multipath *mpp) { char status[PARAMS_SIZE] = {0}; if (!mpp) return 1; if (dm_get_status(mpp->alias, status)) { condlog(3, "%s: cannot get status", mpp->alias); return 1; } if (disassemble_status(status, mpp)) { condlog(3, "%s: cannot disassemble status", mpp->alias); return 1; } return 0; } void sync_paths(struct multipath *mpp, vector pathvec) { struct path *pp; struct pathgroup *pgp; int found, i, j; vector_foreach_slot (mpp->paths, pp, i) { found = 0; vector_foreach_slot(mpp->pg, pgp, j) { if (find_slot(pgp->paths, (void *)pp) != -1) { found = 1; break; } } if (!found) { condlog(3, "%s dropped path %s", mpp->alias, pp->dev); vector_del_slot(mpp->paths, i--); orphan_path(pp, "path removed externally"); } } update_mpp_paths(mpp, pathvec); vector_foreach_slot (mpp->paths, pp, i) pp->mpp = mpp; } extern int update_multipath_strings (struct multipath *mpp, vector pathvec) { if (!mpp) return 1; update_mpp_paths(mpp, pathvec); condlog(4, "%s: %s", mpp->alias, __FUNCTION__); free_multipath_attributes(mpp); free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; if (update_multipath_table(mpp, pathvec)) return 1; sync_paths(mpp, pathvec); if (update_multipath_status(mpp)) return 1; return 0; } extern void set_no_path_retry(struct multipath *mpp) { mpp->retry_tick = 0; mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); select_no_path_retry(mpp); switch (mpp->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; case NO_PATH_RETRY_FAIL: dm_queue_if_no_path(mpp->alias, 0); break; case NO_PATH_RETRY_QUEUE: dm_queue_if_no_path(mpp->alias, 1); break; default: dm_queue_if_no_path(mpp->alias, 1); if (mpp->nr_active == 0) { /* Enter retry mode */ mpp->retry_tick = mpp->no_path_retry * conf->checkint; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); } break; } } extern 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 (!dm_map_present(mpp->alias)) { /* Table has been removed */ condlog(3, "%s: table does not exist", mpp->alias); goto out; } if (update_multipath_strings(mpp, vecs->pathvec)) { condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } set_multipath_wwid(mpp); mpp->mpe = find_mpe(mpp->wwid); condlog(3, "%s: discover", mpp->alias); if (!mpp->hwe) mpp->hwe = extract_hwe_from_path(mpp); if (!mpp->hwe) { condlog(3, "%s: no hardware entry found, using defaults", mpp->alias); } if (reset) { select_rr_weight(mpp); select_pgfailback(mpp); set_no_path_retry(mpp); select_flush_on_last_del(mpp); if (VECTOR_SIZE(mpp->paths) != 0) dm_cancel_deferred_remove(mpp); } return 0; out: remove_map(mpp, vecs, PURGE_VEC); return 1; } extern struct multipath * add_map_without_path (struct vectors * vecs, char * alias) { struct multipath * mpp = alloc_multipath(); if (!mpp || !alias) return NULL; mpp->alias = STRDUP(alias); if (setup_multipath(vecs, mpp)) return NULL; /* mpp freed in setup_multipath */ if (adopt_paths(vecs->pathvec, mpp, 1)) goto out; if (!vector_alloc_slot(vecs->mpvec)) goto out; vector_set_slot(vecs->mpvec, mpp); 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) { struct multipath * mp; int i; vector_foreach_slot (vecs->mpvec, mp, i) if (strcmp(mp->wwid, mpp->wwid) == 0) { strncpy(mpp->alias_old, mp->alias, WWID_SIZE); return; } } extern struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec) { struct multipath * mpp; if (!strlen(pp->wwid)) return NULL; if (!(mpp = alloc_multipath())) return NULL; mpp->mpe = find_mpe(pp->wwid); mpp->hwe = pp->hwe; strcpy(mpp->wwid, pp->wwid); find_existing_alias(mpp, vecs); if (select_alias(mpp)) goto out; mpp->size = pp->size; if (adopt_paths(vecs->pathvec, mpp, 1)) goto out; if (add_vec) { if (!vector_alloc_slot(vecs->mpvec)) goto out; vector_set_slot(vecs->mpvec, mpp); } return mpp; out: remove_map(mpp, vecs, PURGE_VEC); return NULL; } extern int verify_paths(struct multipath * mpp, struct vectors * vecs) { struct path * pp; int count = 0; int i, j; if (!mpp) return 0; vector_foreach_slot (mpp->paths, pp, i) { /* * see if path is in sysfs */ if (sysfs_attr_get_value(pp->udev, "dev", pp->dev_t, BLK_DEV_SIZE) < 0) { if (pp->state != PATH_DOWN) { condlog(1, "%s: removing valid path %s in state %d", mpp->alias, pp->dev, pp->state); } else { condlog(3, "%s: failed to access path %s", mpp->alias, pp->dev); } count++; vector_del_slot(mpp->paths, i); i--; if ((j = find_slot(vecs->pathvec, (void *)pp)) != -1) vector_del_slot(vecs->pathvec, j); free_path(pp); } else { condlog(4, "%s: verified path %s dev_t %s", mpp->alias, pp->dev, pp->dev_t); } } 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) { 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; } } } return 0; } /* * mpp->no_path_retry: * -2 (QUEUE) : queue_if_no_path enabled, never turned off * -1 (FAIL) : fail_if_no_path * 0 (UNDEF) : nothing * >0 : queue_if_no_path enabled, turned off after polling n times */ void update_queue_mode_del_path(struct multipath *mpp) { if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) { /* * Enter retry mode. * meaning of +1: retry_tick may be decremented in * checkerloop before starting retry. */ mpp->stat_queueing_timeouts++; mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); } void update_queue_mode_add_path(struct multipath *mpp) { if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) { /* come back to normal mode from retry mode */ mpp->retry_tick = 0; dm_queue_if_no_path(mpp->alias, 1); condlog(2, "%s: queue_if_no_path enabled", mpp->alias); condlog(1, "%s: Recovered to normal mode", mpp->alias); } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); } multipath-tools-0.5.0+git1.656f8865/libmultipath/structs_vec.h000066400000000000000000000027261260771327300237730ustar00rootroot00000000000000#ifndef _STRUCTS_VEC_H #define _STRUCTS_VEC_H #include "lock.h" /* struct mutex_lock { pthread_mutex_t *mutex; int depth; }; */ struct vectors { struct mutex_lock lock; /* defined in lock.h */ vector pathvec; vector mpvec; }; void set_no_path_retry(struct multipath *mpp); int adopt_paths (vector pathvec, struct multipath * mpp, int get_info); void orphan_paths (vector pathvec, struct multipath * mpp); 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); 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_maps (struct vectors * vecs); void remove_maps_and_stop_waiters (struct vectors * vecs); struct multipath * add_map_without_path (struct vectors * vecs, 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); #endif /* _STRUCTS_VEC_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/switchgroup.c000066400000000000000000000024141260771327300237720ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Edward Goggin, EMC */ #include "checkers.h" #include "vector.h" #include "structs.h" #include "switchgroup.h" extern void path_group_prio_update (struct pathgroup * pgp) { int i; int priority = 0; struct path * pp; pgp->enabled_paths = 0; if (!pgp->paths) { pgp->priority = 0; return; } vector_foreach_slot (pgp->paths, pp, i) { if (pp->state == PATH_UP || pp->state == PATH_GHOST) { priority += pp->priority; pgp->enabled_paths++; } } if (pgp->enabled_paths) pgp->priority = priority / pgp->enabled_paths; else pgp->priority = 0; } extern int select_path_group (struct multipath * mpp) { int i; int max_priority = 0; int bestpg = 1; int max_enabled_paths = 1; struct pathgroup * pgp; if (!mpp->pg) return 1; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; path_group_prio_update(pgp); if (pgp->enabled_paths) { if (pgp->priority > max_priority) { max_priority = pgp->priority; max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } else if (pgp->priority == max_priority) { if (pgp->enabled_paths > max_enabled_paths) { max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } } } } return bestpg; } multipath-tools-0.5.0+git1.656f8865/libmultipath/switchgroup.h000066400000000000000000000001461260771327300237770ustar00rootroot00000000000000void path_group_prio_update (struct pathgroup * pgp); int select_path_group (struct multipath * mpp); multipath-tools-0.5.0+git1.656f8865/libmultipath/sysfs.c000066400000000000000000000153361260771327300225720ustar00rootroot00000000000000/* * Copyright (C) 2005-2006 Kay Sievers * * 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 version 2 of the License. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "sysfs.h" #include "list.h" #include "util.h" #include "debug.h" #include "devmapper.h" /* * When we modify an attribute value we cannot rely on libudev for now, * as libudev lacks the capability to update an attribute value. * So for modified attributes we need to implement our own function. */ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); if (stat(devpath, &statbuf) != 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); return -ENXIO; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IRUSR) == 0) { condlog(4, "%s is not readable", devpath); return -EPERM; } /* read attribute value */ fd = open(devpath, O_RDONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } size = read(fd, value, value_len); if (size < 0) { condlog(4, "read from %s failed: %s", devpath, strerror(errno)); size = -errno; } else if (size == value_len) { condlog(4, "overflow while reading from %s", devpath); size = 0; } else { value[size] = '\0'; } close(fd); if (size > 0) size = strchop(value); return size; } ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, unsigned char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); if (stat(devpath, &statbuf) != 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); return -ENXIO; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IRUSR) == 0) { condlog(4, "%s is not readable", devpath); return -EPERM; } /* read attribute value */ fd = open(devpath, O_RDONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } size = read(fd, value, value_len); if (size < 0) { condlog(4, "read from %s failed: %s", devpath, strerror(errno)); size = -errno; } else if (size == value_len) { condlog(4, "overflow while reading from %s", devpath); size = 0; } close(fd); return size; } ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value || !value_len) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); if (stat(devpath, &statbuf) != 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); return -errno; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IWUSR) == 0) { condlog(4, "%s is not writeable", devpath); return -EPERM; } /* write attribute value */ fd = open(devpath, O_WRONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } size = write(fd, value, value_len); if (size < 0) { condlog(4, "write to %s failed: %s", devpath, strerror(errno)); size = -errno; } else if (size < value_len) { condlog(4, "tried to write %ld to %s. Wrote %ld", (long)value_len, devpath, (long)size); size = 0; } close(fd); return size; } int sysfs_get_size (struct path *pp, unsigned long long * size) { char attr[255]; int r; if (!pp->udev || !size) return 1; attr[0] = '\0'; if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) { condlog(3, "%s: No size attribute in sysfs", pp->dev); return 1; } r = sscanf(attr, "%llu\n", size); if (r != 1) { condlog(3, "%s: Cannot parse size attribute", pp->dev); *size = 0; return 1; } return 0; } int sysfs_check_holders(char * check_devt, char * new_devt) { unsigned int major, new_minor, table_minor; char path[PATH_SIZE], check_dev[PATH_SIZE]; char * table_name; DIR *dirfd; struct dirent *holder; if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) { condlog(1, "invalid device number %s", new_devt); return 0; } if (devt2devname(check_dev, PATH_SIZE, check_devt)) { condlog(1, "can't get devname for %s", check_devt); return 0; } condlog(3, "%s: checking holder", check_dev); snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev); dirfd = opendir(path); if (dirfd == NULL) { condlog(3, "%s: failed to open directory %s (%d)", check_dev, path, errno); return 0; } while ((holder = readdir(dirfd)) != NULL) { if ((strcmp(holder->d_name,".") == 0) || (strcmp(holder->d_name,"..") == 0)) continue; if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) { condlog(3, "%s: %s is not a dm-device", check_dev, holder->d_name); continue; } if (table_minor == new_minor) { condlog(3, "%s: holder already correct", check_dev); continue; } table_name = dm_mapname(major, table_minor); condlog(0, "%s: reassign table %s old %s new %s", check_dev, table_name, check_devt, new_devt); dm_reassign_table(table_name, check_devt, new_devt); FREE(table_name); } closedir(dirfd); return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/sysfs.h000066400000000000000000000011011260771327300225600ustar00rootroot00000000000000/* * sysfs.h */ #ifndef _LIBMULTIPATH_SYSFS_H #define _LIBMULTIPATH_SYSFS_H ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len); ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len); 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); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/uevent.c000066400000000000000000000367521260771327300227360ustar00rootroot00000000000000/* * uevent.c - trigger upon netlink uevents from the kernel * * Only kernels from version 2.6.10* on provide the uevent netlink socket. * Until the libc-kernel-headers are updated, you need to compile with: * * gcc -I /lib/modules/`uname -r`/build/include -o uevent_listen uevent_listen.c * * Copyright (C) 2004 Kay Sievers * * 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 version 2 of the License. * * 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., * 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "memory.h" #include "debug.h" #include "list.h" #include "uevent.h" #include "vector.h" typedef int (uev_trigger)(struct uevent *, void * trigger_data); pthread_t uevq_thr; LIST_HEAD(uevq); pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t *uevq_lockp = &uevq_lock; pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t *uev_condp = &uev_cond; uev_trigger *my_uev_trigger; void * my_trigger_data; int servicing_uev; int is_uevent_busy(void) { int empty; pthread_mutex_lock(uevq_lockp); empty = list_empty(&uevq); pthread_mutex_unlock(uevq_lockp); return (!empty || servicing_uev); } struct uevent * alloc_uevent (void) { struct uevent *uev = MALLOC(sizeof(struct uevent)); if (uev) INIT_LIST_HEAD(&uev->node); return uev; } void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached) { if (pthread_attr_init(attr)) { fprintf(stderr, "can't initialize thread attr: %s\n", strerror(errno)); exit(1); } if (stacksize < PTHREAD_STACK_MIN) stacksize = PTHREAD_STACK_MIN; if (pthread_attr_setstacksize(attr, stacksize)) { fprintf(stderr, "can't set thread stack size to %lu: %s\n", (unsigned long)stacksize, strerror(errno)); exit(1); } if (detached && pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)) { fprintf(stderr, "can't set thread to detached: %s\n", strerror(errno)); exit(1); } } /* * Called with uevq_lockp held */ void service_uevq(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data)) condlog(0, "uevent trigger error"); if (uev->udev) udev_device_unref(uev->udev); FREE(uev); } } static void uevq_stop(void *arg) { struct udev *udev = arg; condlog(3, "Stopping uev queue"); pthread_mutex_lock(uevq_lockp); my_uev_trigger = NULL; pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevq_lockp); udev_unref(udev); } void uevq_cleanup(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); FREE(uev); } } /* * Service the uevent queue. */ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data), void * trigger_data) { my_uev_trigger = uev_trigger; my_trigger_data = trigger_data; mlockall(MCL_CURRENT | MCL_FUTURE); while (1) { LIST_HEAD(uevq_tmp); pthread_mutex_lock(uevq_lockp); servicing_uev = 0; /* * Condition signals are unreliable, * so make sure we only wait if we have to. */ if (list_empty(&uevq)) { pthread_cond_wait(uev_condp, uevq_lockp); } servicing_uev = 1; list_splice_init(&uevq, &uevq_tmp); pthread_mutex_unlock(uevq_lockp); if (!my_uev_trigger) break; service_uevq(&uevq_tmp); } condlog(3, "Terminating uev service queue"); uevq_cleanup(&uevq); return 0; } struct uevent *uevent_from_buffer(char *buf, ssize_t buflen) { struct uevent *uev; char *buffer; size_t bufpos; int i; char *pos; uev = alloc_uevent(); if (!uev) { condlog(1, "lost uevent, oom"); return NULL; } if ((size_t)buflen > sizeof(buf)-1) buflen = sizeof(buf)-1; /* * Copy the shared receive buffer contents to buffer private * to this uevent so we can immediately reuse the shared buffer. */ memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE); buffer = uev->buffer; buffer[buflen] = '\0'; /* save start of payload */ bufpos = strlen(buffer) + 1; /* action string */ uev->action = buffer; pos = strchr(buffer, '@'); if (!pos) { condlog(3, "bad action string '%s'", buffer); FREE(uev); return NULL; } pos[0] = '\0'; /* sysfs path */ uev->devpath = &pos[1]; /* hotplug events have the environment attached - reconstruct envp[] */ for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) { int keylen; char *key; key = &buffer[bufpos]; keylen = strlen(key); uev->envp[i] = key; /* Filter out sequence number */ if (strncmp(key, "SEQNUM=", 7) == 0) { char *eptr; uev->seqnum = strtoul(key + 7, &eptr, 10); if (eptr == key + 7) uev->seqnum = -1; } bufpos += keylen + 1; } uev->envp[i] = NULL; condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum, uev->action, uev->devpath); uev->kernel = strrchr(uev->devpath, '/'); if (uev->kernel) uev->kernel++; /* print payload environment */ for (i = 0; uev->envp[i] != NULL; i++) condlog(5, "%s", uev->envp[i]); return uev; } int failback_listen(void) { int sock; struct sockaddr_nl snl; struct sockaddr_un sun; socklen_t addrlen; int retval; int rcvbufsz = 128*1024; int rcvsz = 0; int rcvszsz = sizeof(rcvsz); unsigned int *prcvszsz = (unsigned int *)&rcvszsz; const int feature_on = 1; /* * First check whether we have a udev socket */ memset(&sun, 0x00, sizeof(struct sockaddr_un)); sun.sun_family = AF_LOCAL; strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event"); addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1; sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (sock >= 0) { condlog(3, "reading events from udev socket."); /* the bind takes care of ensuring only one copy running */ retval = bind(sock, (struct sockaddr *) &sun, addrlen); if (retval < 0) { condlog(0, "bind failed, exit"); goto exit; } /* enable receiving of the sender credentials */ setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); } else { /* Fallback to read kernel netlink events */ memset(&snl, 0x00, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 0x01; sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (sock == -1) { condlog(0, "error getting socket, exit"); return 1; } condlog(3, "reading events from kernel."); /* * try to avoid dropping uevents, even so, this is not a guarantee, * but it does help to change the netlink uevent socket's * receive buffer threshold from the default value of 106,496 to * the maximum value of 262,142. */ retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, sizeof(rcvbufsz)); if (retval < 0) { condlog(0, "error setting receive buffer size for socket, exit"); exit(1); } retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); if (retval < 0) { condlog(0, "error setting receive buffer size for socket, exit"); exit(1); } condlog(3, "receive buffer size for socket is %u.", rcvsz); /* enable receiving of the sender credentials */ setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); retval = bind(sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); if (retval < 0) { condlog(0, "bind failed, exit"); goto exit; } } while (1) { size_t bufpos; ssize_t buflen; struct uevent *uev; struct msghdr smsg; struct iovec iov; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *cmsg; struct ucred *cred; static char buf[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE]; memset(buf, 0x00, sizeof(buf)); iov.iov_base = &buf; iov.iov_len = sizeof(buf); memset (&smsg, 0x00, sizeof(struct msghdr)); smsg.msg_iov = &iov; smsg.msg_iovlen = 1; smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); buflen = recvmsg(sock, &smsg, 0); if (buflen < 0) { if (errno != EINTR) condlog(0, "error receiving message, errno %d", errno); continue; } cmsg = CMSG_FIRSTHDR(&smsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { condlog(3, "no sender credentials received, message ignored"); continue; } cred = (struct ucred *)CMSG_DATA(cmsg); if (cred->uid != 0) { condlog(3, "sender uid=%d, message ignored", cred->uid); continue; } /* skip header */ bufpos = strlen(buf) + 1; if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) { condlog(3, "invalid message length"); continue; } /* check message header */ if (strstr(buf, "@/") == NULL) { condlog(3, "unrecognized message header"); continue; } if ((size_t)buflen > sizeof(buf)-1) { condlog(2, "buffer overflow for received uevent"); buflen = sizeof(buf)-1; } uev = uevent_from_buffer(buf, buflen); if (!uev) continue; /* * Queue uevent and poke service pthread. */ pthread_mutex_lock(uevq_lockp); list_add_tail(&uev->node, &uevq); pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevq_lockp); } exit: close(sock); return 1; } struct uevent *uevent_from_udev_device(struct udev_device *dev) { struct uevent *uev; int i = 0; char *pos, *end; struct udev_list_entry *list_entry; uev = alloc_uevent(); if (!uev) { udev_device_unref(dev); condlog(1, "lost uevent, oom"); return NULL; } pos = uev->buffer; end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) { const char *name, *value; int bytes; name = udev_list_entry_get_name(list_entry); if (!name) name = "(null)"; value = udev_list_entry_get_value(list_entry); if (!value) value = "(null)"; bytes = snprintf(pos, end - pos, "%s=%s", name, value); if (pos + bytes >= end) { condlog(2, "buffer overflow for uevent"); break; } uev->envp[i] = pos; pos += bytes; *pos = '\0'; pos++; if (strcmp(name, "DEVPATH") == 0) uev->devpath = uev->envp[i] + 8; if (strcmp(name, "ACTION") == 0) uev->action = uev->envp[i] + 7; i++; if (i == HOTPLUG_NUM_ENVP - 1) break; } uev->udev = dev; uev->envp[i] = NULL; condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath); uev->kernel = strrchr(uev->devpath, '/'); if (uev->kernel) uev->kernel++; /* print payload environment */ for (i = 0; uev->envp[i] != NULL; i++) condlog(5, "%s", uev->envp[i]); return uev; } int uevent_listen(struct udev *udev) { int err = 2; struct udev_monitor *monitor = NULL; int fd, fd_ep = -1, socket_flags, events; int need_failback = 1; int timeout = 30; sigset_t mask; LIST_HEAD(uevlisten_tmp); /* * Queue uevents for service by dedicated thread so that the uevent * listening thread does not block on multipathd locks (vecs->lock) * thereby not getting to empty the socket's receive buffer queue * often enough. */ if (!udev) { condlog(1, "no udev context"); return 1; } udev_ref(udev); pthread_cleanup_push(uevq_stop, udev); monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!monitor) { condlog(2, "failed to create udev monitor"); goto out; } #ifdef LIBUDEV_API_RECVBUF if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024)) condlog(2, "failed to increase buffer size"); #endif fd = udev_monitor_get_fd(monitor); if (fd < 0) { condlog(2, "failed to get monitor fd"); goto out; } socket_flags = fcntl(fd, F_GETFL); if (socket_flags < 0) { condlog(2, "failed to get monitor socket flags : %s", strerror(errno)); goto out; } if (fcntl(fd, F_SETFL, socket_flags & ~O_NONBLOCK) < 0) { condlog(2, "failed to set monitor socket flags : %s", strerror(errno)); goto out; } err = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL); if (err) condlog(2, "failed to create filter : %s", strerror(-err)); err = udev_monitor_enable_receiving(monitor); if (err) { condlog(2, "failed to enable receiving : %s", strerror(-err)); goto out; } pthread_sigmask(SIG_SETMASK, NULL, &mask); sigdelset(&mask, SIGHUP); sigdelset(&mask, SIGUSR1); events = 0; while (1) { struct uevent *uev; struct udev_device *dev; struct pollfd ev_poll; struct timespec poll_timeout; int fdcount; memset(&ev_poll, 0, sizeof(struct pollfd)); ev_poll.fd = fd; ev_poll.events = POLLIN; memset(&poll_timeout, 0, sizeof(struct timespec)); poll_timeout.tv_sec = timeout; errno = 0; fdcount = ppoll(&ev_poll, 1, &poll_timeout, &mask); if (fdcount && ev_poll.revents & POLLIN) { timeout = 0; dev = udev_monitor_receive_device(monitor); if (!dev) { condlog(0, "failed getting udev device"); continue; } uev = uevent_from_udev_device(dev); if (!uev) continue; list_add_tail(&uev->node, &uevlisten_tmp); events++; continue; } if (fdcount < 0) { if (errno != EINTR) condlog(0, "error receiving " "uevent message: %m"); err = -errno; break; } if (!list_empty(&uevlisten_tmp)) { /* * Queue uevents and poke service pthread. */ condlog(3, "Forwarding %d uevents", events); pthread_mutex_lock(uevq_lockp); list_splice_tail_init(&uevlisten_tmp, &uevq); pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevq_lockp); events = 0; } timeout = 30; } need_failback = 0; out: if (fd_ep >= 0) close(fd_ep); if (monitor) udev_monitor_unref(monitor); if (need_failback) err = failback_listen(); pthread_cleanup_pop(1); return err; } extern int uevent_get_major(struct uevent *uev) { char *p, *q; int i, major = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "MAJOR", 5) && strlen(uev->envp[i]) > 6) { p = uev->envp[i] + 6; major = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid major '%s'", p); major = -1; } break; } } return major; } extern int uevent_get_minor(struct uevent *uev) { char *p, *q; int i, minor = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "MINOR", 5) && strlen(uev->envp[i]) > 6) { p = uev->envp[i] + 6; minor = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid minor '%s'", p); minor = -1; } break; } } return minor; } extern int uevent_get_disk_ro(struct uevent *uev) { char *p, *q; int i, ro = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DISK_RO", 6) && strlen(uev->envp[i]) > 7) { p = uev->envp[i] + 8; ro = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid read_only setting '%s'", p); ro = -1; } break; } } return ro; } extern char * uevent_get_dm_name(struct uevent *uev) { char *p = NULL; int i; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DM_NAME", 6) && strlen(uev->envp[i]) > 7) { p = MALLOC(strlen(uev->envp[i] + 8) + 1); strcpy(p, uev->envp[i] + 8); break; } } return p; } multipath-tools-0.5.0+git1.656f8865/libmultipath/uevent.h000066400000000000000000000017261260771327300227340ustar00rootroot00000000000000#ifndef _UEVENT_H #define _UEVENT_H /* * buffer for environment variables, the kernel's size in * lib/kobject_uevent.c should fit in */ #define HOTPLUG_BUFFER_SIZE 2048 #define HOTPLUG_NUM_ENVP 32 #define OBJECT_SIZE 512 #ifndef NETLINK_KOBJECT_UEVENT #define NETLINK_KOBJECT_UEVENT 15 #endif struct udev; struct uevent { struct list_head node; struct udev_device *udev; char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE]; char *devpath; char *action; char *kernel; unsigned long seqnum; char *envp[HOTPLUG_NUM_ENVP]; }; int is_uevent_busy(void); void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached); int uevent_listen(struct udev *udev); int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data), void * trigger_data); int uevent_get_major(struct uevent *uev); int uevent_get_minor(struct uevent *uev); int uevent_get_disk_ro(struct uevent *uev); char *uevent_get_dm_name(struct uevent *uev); #endif /* _UEVENT_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/util.c000066400000000000000000000107541260771327300223770ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "memory.h" #include "checkers.h" #include "vector.h" #include "structs.h" size_t strchop(char *str) { int i; for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ; str[++i] = '\0'; return strlen(str); } int basenamecpy (const char * str1, char * str2, int str2len) { char *p; if (!str1 || !strlen(str1)) return 0; if (strlen(str1) >= str2len) return 0; if (!str2) return 0; p = (char *)str1 + (strlen(str1) - 1); while (*--p != '/' && p != str1) continue; if (p != str1) p++; strncpy(str2, p, str2len); str2[str2len - 1] = '\0'; return strchop(str2); } int filepresent (char * run) { struct stat buf; if(!stat(run, &buf)) return 1; return 0; } int get_word (char * sentence, char ** word) { char * p; int len; int skip = 0; if (word) *word = NULL; while (*sentence == ' ') { sentence++; skip++; } if (*sentence == '\0') return 0; p = sentence; while (*p != ' ' && *p != '\0') p++; len = (int) (p - sentence); if (!word) return skip + len; *word = MALLOC(len + 1); if (!*word) { condlog(0, "get_word : oom"); return 0; } strncpy(*word, sentence, len); strchop(*word); condlog(4, "*word = %s, len = %i", *word, len); if (*p == '\0') return 0; return skip + len; } size_t strlcpy(char *dst, const char *src, size_t size) { size_t bytes = 0; char *q = dst; const char *p = src; char ch; while ((ch = *p++)) { if (bytes+1 < size) *q++ = ch; bytes++; } /* If size == 0 there is no space for a final null... */ if (size) *q = '\0'; return bytes; } size_t strlcat(char *dst, const char *src, size_t size) { size_t bytes = 0; char *q = dst; const char *p = src; char ch; while (bytes < size && *q) { q++; bytes++; } if (bytes == size) return (bytes + strlen(src)); while ((ch = *p++)) { if (bytes+1 < size) *q++ = ch; bytes++; } *q = '\0'; return bytes; } extern int devt2devname (char *devname, int devname_len, char *devt) { FILE *fd; unsigned int tmpmaj, tmpmin, major, minor; char dev[FILE_NAME_SIZE]; char block_path[PATH_SIZE]; struct stat statbuf; memset(block_path, 0, sizeof(block_path)); memset(dev, 0, sizeof(dev)); if (sscanf(devt, "%u:%u", &major, &minor) != 2) { condlog(0, "Invalid device number %s", devt); return 1; } if (devname_len > FILE_NAME_SIZE) devname_len = FILE_NAME_SIZE; if (stat("/sys/dev/block", &statbuf) == 0) { /* Newer kernels have /sys/dev/block */ sprintf(block_path,"/sys/dev/block/%u:%u", major, minor); if (lstat(block_path, &statbuf) == 0) { if (S_ISLNK(statbuf.st_mode) && readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) { char *p = strrchr(dev, '/'); if (!p) { condlog(0, "No sysfs entry for %s", block_path); return 1; } p++; strncpy(devname, p, devname_len); return 0; } } goto skip_proc; } memset(block_path, 0, sizeof(block_path)); if (!(fd = fopen("/proc/partitions", "r"))) { condlog(0, "Cannot open /proc/partitions"); return 1; } while (!feof(fd)) { int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev); if (!r) { r = fscanf(fd,"%*s\n"); continue; } if (r != 3) continue; if ((major == tmpmaj) && (minor == tmpmin)) { if (snprintf(block_path, sizeof(block_path), "/sys/block/%s", dev) >= sizeof(block_path)) { condlog(0, "device name %s is too long", dev); fclose(fd); return 1; } break; } } fclose(fd); skip_proc: if (strncmp(block_path,"/sys/block", 10)) { condlog(3, "No device found for %u:%u", major, minor); return 1; } if (stat(block_path, &statbuf) < 0) { condlog(0, "No sysfs entry for %s", block_path); return 1; } if (S_ISDIR(statbuf.st_mode) == 0) { condlog(0, "sysfs entry %s is not a directory", block_path); return 1; } basenamecpy((const char *)block_path, devname, devname_len); return 0; } /* This function returns a pointer inside of the supplied pathname string. * If is_path_device is true, it may also modify the supplied string */ char *convert_dev(char *name, int is_path_device) { char *ptr; if (!name) return NULL; if (is_path_device) { ptr = strstr(name, "cciss/"); if (ptr) { ptr += 5; *ptr = '!'; } } if (!strncmp(name, "/dev/", 5) && strlen(name) > 5) ptr = name + 5; else ptr = name; return ptr; } dev_t parse_devt(const char *dev_t) { int maj, min; if (sscanf(dev_t,"%d:%d", &maj, &min) != 2) return 0; return makedev(maj, min); } multipath-tools-0.5.0+git1.656f8865/libmultipath/util.h000066400000000000000000000012201260771327300223700ustar00rootroot00000000000000#ifndef _UTIL_H #define _UTIL_H size_t strchop(char *); int basenamecpy (const char * src, char * dst, int); int filepresent (char * run); int get_word (char * sentence, char ** word); size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); int devt2devname (char *, int, char *); dev_t parse_devt(const char *dev_t); char *convert_dev(char *dev, int is_path_device); #define safe_sprintf(var, format, args...) \ snprintf(var, sizeof(var), format, ##args) >= sizeof(var) #define safe_snprintf(var, size, format, args...) \ snprintf(var, size, format, ##args) >= size #endif /* _UTIL_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/uxsock.c000066400000000000000000000102231260771327300227250ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Alasdair Kergon, Redhat */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SYSTEMD #include #endif #include "memory.h" #include "uxsock.h" #include "debug.h" /* * connect to a unix domain socket */ int ux_socket_connect(const char *name) { int fd, len; struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; len = strlen(name) + 1 + sizeof(sa_family_t); strncpy(&addr.sun_path[1], name, len); fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { condlog(3, "Couldn't create ux_socket, error %d", errno); return -1; } if (connect(fd, (struct sockaddr *)&addr, len) == -1) { condlog(3, "Couldn't connect to ux_socket, error %d", errno); close(fd); return -1; } return fd; } /* * create a unix domain socket and start listening on it * return a file descriptor open on the socket */ int ux_socket_listen(const char *name) { int fd, len; #ifdef USE_SYSTEMD int num; #endif struct sockaddr_un addr; #ifdef USE_SYSTEMD num = sd_listen_fds(0); if (num > 1) { condlog(3, "sd_listen_fds returned %d fds", num); return -1; } else if (num == 1) { fd = SD_LISTEN_FDS_START + 0; condlog(3, "using fd %d from sd_listen_fds", fd); return fd; } #endif fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { condlog(3, "Couldn't create ux_socket, error %d", errno); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; len = strlen(name) + 1 + sizeof(sa_family_t); strncpy(&addr.sun_path[1], name, len); if (bind(fd, (struct sockaddr *)&addr, len) == -1) { condlog(3, "Couldn't bind to ux_socket, error %d", errno); close(fd); return -1; } if (listen(fd, 10) == -1) { condlog(3, "Couldn't listen to ux_socket, error %d", errno); close(fd); return -1; } return fd; } /* * keep writing until it's all sent */ size_t write_all(int fd, const void *buf, size_t len) { size_t total = 0; while (len) { ssize_t n = write(fd, buf, len); if (n < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; return total; } if (!n) return total; buf = n + (char *)buf; len -= n; total += n; } return total; } /* * keep reading until its all read */ ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout) { size_t total = 0; ssize_t n; int ret; struct pollfd pfd; while (len) { pfd.fd = fd; pfd.events = POLLIN; ret = poll(&pfd, 1, timeout); if (!ret) { return -ETIMEDOUT; } else if (ret < 0) { if (errno == EINTR) continue; return -errno; } else if (!pfd.revents & POLLIN) continue; n = read(fd, buf, len); if (n < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; return -errno; } if (!n) return total; buf = n + (char *)buf; len -= n; total += n; } return total; } /* * send a packet in length prefix format */ int send_packet(int fd, const char *buf, size_t len) { int ret = 0; sigset_t set, old; /* Block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); pthread_sigmask(SIG_BLOCK, &set, &old); if (write_all(fd, &len, sizeof(len)) != sizeof(len)) ret = -1; if (!ret && write_all(fd, buf, len) != len) ret = -1; /* And unblock it again */ pthread_sigmask(SIG_SETMASK, &old, NULL); return ret; } /* * receive a packet in length prefix format */ int recv_packet(int fd, char **buf, size_t *len, unsigned int timeout) { ssize_t ret; ret = read_all(fd, len, sizeof(*len), timeout); if (ret < 0) { (*buf) = NULL; *len = 0; return ret; } if (ret < sizeof(*len)) { (*buf) = NULL; *len = 0; return -EIO; } if (len == 0) { (*buf) = NULL; return 0; } (*buf) = MALLOC(*len); if (!*buf) return -ENOMEM; ret = read_all(fd, *buf, *len, timeout); if (ret != *len) { FREE(*buf); (*buf) = NULL; *len = 0; return ret < 0 ? ret : -EIO; } return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/uxsock.h000066400000000000000000000005431260771327300227360ustar00rootroot00000000000000/* some prototypes */ int ux_socket_connect(const char *name); int ux_socket_listen(const char *name); int send_packet(int fd, const char *buf, size_t len); int recv_packet(int fd, char **buf, size_t *len, unsigned int timeout); size_t write_all(int fd, const void *buf, size_t len); ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout); multipath-tools-0.5.0+git1.656f8865/libmultipath/vector.c000066400000000000000000000065241260771327300227240ustar00rootroot00000000000000/* * Part: Vector structure manipulation. * * Version: $Id: vector.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * 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. * * Copyright (c) 2002, 2003, 2004 Alexandre Cassen * Copyright (c) 2005 Christophe Varoqui */ #include "memory.h" #include #include "vector.h" /* * Initialize vector struct. * allocated 'size' slot elements then return vector. */ vector vector_alloc(void) { vector v = (vector) MALLOC(sizeof (struct _vector)); return v; } /* allocated one slot */ void * vector_alloc_slot(vector v) { void *new_slot = NULL; if (!v) return NULL; v->allocated += VECTOR_DEFAULT_SIZE; if (v->slot) new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated); else new_slot = (void *) MALLOC(sizeof (void *) * v->allocated); if (!new_slot) v->allocated -= VECTOR_DEFAULT_SIZE; else v->slot = new_slot; return v->slot; } int vector_move_up(vector v, int src, int dest) { void *value; int i; if (dest == src) return 0; if (dest > src || src >= v->allocated) return -1; value = v->slot[src]; for (i = src - 1; i >= dest; i--) v->slot[i + 1] = v->slot[i]; v->slot[dest] = value; return 0; } void * vector_insert_slot(vector v, int slot, void *value) { int i; if (!vector_alloc_slot(v)) return NULL; for (i = VECTOR_SIZE(v) - 2; i >= slot; i--) v->slot[i + 1] = v->slot[i]; v->slot[slot] = value; return v->slot[slot]; } int find_slot(vector v, void * addr) { int i; if (!v) return -1; for (i = 0; i < VECTOR_SIZE(v); i++) if (v->slot[i] == addr) return i; return -1; } void vector_del_slot(vector v, int slot) { int i; if (!v || !v->allocated || slot < 0 || slot > VECTOR_SIZE(v)) return; for (i = slot + 1; i < VECTOR_SIZE(v); i++) v->slot[i-1] = v->slot[i]; v->allocated -= VECTOR_DEFAULT_SIZE; if (v->allocated <= 0) { FREE(v->slot); v->slot = NULL; v->allocated = 0; } else { void *new_slot; new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated); if (!new_slot) v->allocated += VECTOR_DEFAULT_SIZE; else v->slot = new_slot; } } void vector_repack(vector v) { int i; if (!v || !v->allocated) return; for (i = 0; i < VECTOR_SIZE(v); i++) if (i > 0 && v->slot[i] == NULL) vector_del_slot(v, i--); } /* Free memory vector allocation */ void vector_free(vector v) { if (!v) return; if (v->slot) FREE(v->slot); v->allocated = 0; v->slot = NULL; FREE(v); } void free_strvec(vector strvec) { int i; char *str; if (!strvec) return; vector_foreach_slot (strvec, str, i) if (str) FREE(str); vector_free(strvec); } /* Set a vector slot value */ void vector_set_slot(vector v, void *value) { unsigned int i; if (!v) return; i = VECTOR_SIZE(v) - 1; v->slot[i] = value; } multipath-tools-0.5.0+git1.656f8865/libmultipath/vector.h000066400000000000000000000044061260771327300227260ustar00rootroot00000000000000/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vector.c include file. * * Version: $Id: vector.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * 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. */ #ifndef _VECTOR_H #define _VECTOR_H /* vector definition */ struct _vector { int allocated; void **slot; }; typedef struct _vector *vector; #define VECTOR_DEFAULT_SIZE 1 #define VECTOR_SIZE(V) ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0) #define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V)) ? (V)->slot[(E)] : NULL) #define VECTOR_LAST_SLOT(V) (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL) #define vector_foreach_slot(v,p,i) \ for (i = 0; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_after(v,p,i) \ for (; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_backwards(v,p,i) \ for (i = VECTOR_SIZE(v); i > 0 && ((p) = (v)->slot[i-1]); i--) /* Prototypes */ extern vector vector_alloc(void); extern void *vector_alloc_slot(vector v); extern void vector_free(vector v); extern void free_strvec(vector strvec); extern void vector_set_slot(vector v, void *value); extern void vector_del_slot(vector v, int slot); extern void *vector_insert_slot(vector v, int slot, void *value); int find_slot(vector v, void * addr); extern void vector_repack(vector v); extern void vector_dump(vector v); extern void dump_strvec(vector strvec); extern int vector_move_up(vector v, int src, int dest); #endif multipath-tools-0.5.0+git1.656f8865/libmultipath/version.h000066400000000000000000000023621260771327300231100ustar00rootroot00000000000000/* * Soft: multipath device mapper target autoconfig * * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ * * Author: Christophe Varoqui * * 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. * * 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. * * Copyright (c) 2006 Christophe Varoqui */ #ifndef _VERSION_H #define _VERSION_H #define VERSION_CODE 0x000500 #define DATE_CODE 0x0c110d #define PROG "multipath-tools" #define MULTIPATH_VERSION(version) \ (version >> 16) & 0xFF, \ (version >> 8) & 0xFF, \ version & 0xFF #define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ MULTIPATH_VERSION(VERSION_CODE), \ MULTIPATH_VERSION(DATE_CODE) #endif /* _VERSION_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/waiter.c000066400000000000000000000105671260771327300227170ustar00rootroot00000000000000/* * 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 "vector.h" #include "memory.h" #include "checkers.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; 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; } void free_waiter (void *data) { struct event_thread *wp = (struct event_thread *)data; if (wp->dmt) dm_task_destroy(wp->dmt); 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* */ 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 = 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 */ } 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); 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); 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; } multipath-tools-0.5.0+git1.656f8865/libmultipath/waiter.h000066400000000000000000000010111260771327300227040ustar00rootroot00000000000000#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; }; struct event_thread * alloc_waiter (void); void free_waiter (void *data); void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs); int start_waiter_thread (struct multipath *mpp, struct vectors *vecs); int waiteventloop (struct event_thread *waiter); void *waitevent (void *et); #endif /* _WAITER_H */ multipath-tools-0.5.0+git1.656f8865/libmultipath/wwids.c000066400000000000000000000142251260771327300225540ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "uxsock.h" #include "file.h" #include "wwids.h" #include "defaults.h" #include "config.h" /* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ static int lookup_wwid(FILE *f, char *wwid) { int c; char buf[LINE_MAX]; int count; while ((c = fgetc(f)) != EOF){ if (c != '/') { if (fgets(buf, LINE_MAX, f) == NULL) return 0; else continue; } count = 0; while ((c = fgetc(f)) != '/') { if (c == EOF) return 0; if (count >= WWID_SIZE - 1) goto next; if (wwid[count] == '\0') goto next; if (c != wwid[count++]) goto next; } if (wwid[count] == '\0') return 1; next: if (fgets(buf, LINE_MAX, f) == NULL) return 0; } return 0; } static int write_out_wwid(int fd, char *wwid) { int ret; off_t offset; char buf[WWID_SIZE + 3]; ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid); if (ret >= (WWID_SIZE + 3) || ret < 0){ condlog(0, "can't format wwid for writing (%d) : %s", ret, strerror(errno)); return -1; } offset = lseek(fd, 0, SEEK_END); if (offset < 0) { condlog(0, "can't seek to the end of wwids file : %s", strerror(errno)); return -1; } if (write_all(fd, buf, strlen(buf)) != strlen(buf)) { condlog(0, "cannot write wwid to wwids file : %s", strerror(errno)); if (ftruncate(fd, offset)) condlog(0, "cannot truncate failed wwid write : %s", strerror(errno)); return -1; } return 1; } int replace_wwids(vector mp) { int i, fd, can_write; struct multipath * mpp; size_t len; int ret = -1; fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); if (fd < 0) goto out; if (!can_write) { condlog(0, "cannot replace wwids. wwids file is read-only"); goto out_file; } if (ftruncate(fd, 0) < 0) { condlog(0, "cannot truncate wwids file : %s", strerror(errno)); goto out_file; } if (lseek(fd, 0, SEEK_SET) < 0) { condlog(0, "cannot seek to the start of the file : %s", strerror(errno)); goto out_file; } len = strlen(WWIDS_FILE_HEADER); if (write_all(fd, WWIDS_FILE_HEADER, len) != len) { condlog(0, "Can't write wwid file header : %s", strerror(errno)); /* cleanup partially written header */ if (ftruncate(fd, 0) < 0) condlog(0, "Cannot truncate header : %s", strerror(errno)); goto out_file; } if (!mp || !mp->allocated) { ret = 0; goto out_file; } vector_foreach_slot(mp, mpp, i) { if (write_out_wwid(fd, mpp->wwid) < 0) goto out_file; } ret = 0; out_file: close(fd); out: return ret; } int do_remove_wwid(int fd, char *str) { char buf[4097]; char *ptr; off_t start = 0; int bytes; while (1) { if (lseek(fd, start, SEEK_SET) < 0) { condlog(0, "wwid file read lseek failed : %s", strerror(errno)); return -1; } bytes = read(fd, buf, 4096); if (bytes < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to read from wwids file : %s", strerror(errno)); return -1; } if (!bytes) /* didn't find wwid to remove */ return 1; buf[bytes] = '\0'; ptr = strstr(buf, str); if (ptr != NULL) { condlog(3, "found '%s'", str); if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) { condlog(0, "write lseek failed : %s", strerror(errno)); return -1; } while (1) { if (write(fd, "#", 1) < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to write to wwids file : %s", strerror(errno)); return -1; } return 0; } } ptr = strrchr(buf, '\n'); if (ptr == NULL) { /* shouldn't happen, assume it is EOF */ condlog(4, "couldn't find newline, assuming end of file"); return 1; } start = start + (ptr - buf) + 1; } } int remove_wwid(char *wwid) { int fd, len, can_write; char *str; int ret = -1; len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */ str = malloc(len); if (str == NULL) { condlog(0, "can't allocate memory to remove wwid : %s", strerror(errno)); return -1; } if (snprintf(str, len, "/%s/\n", wwid) >= len) { condlog(0, "string overflow trying to remove wwid"); goto out; } condlog(3, "removing line '%s' from wwids file", str); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); if (fd < 0) goto out; if (!can_write) { condlog(0, "cannot remove wwid. wwids file is read-only"); goto out_file; } ret = do_remove_wwid(fd, str); out_file: close(fd); out: free(str); return ret; } int check_wwids_file(char *wwid, int write_wwid) { int fd, can_write, found, ret; FILE *f; fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); if (fd < 0) return -1; f = fdopen(fd, "r"); if (!f) { condlog(0,"can't fdopen wwids file : %s", strerror(errno)); close(fd); return -1; } found = lookup_wwid(f, wwid); if (found) { ret = 0; goto out; } if (!write_wwid) { ret = -1; goto out; } if (!can_write) { condlog(0, "wwids file is read-only. Can't write wwid"); ret = -1; goto out; } if (fflush(f) != 0) { condlog(0, "cannot fflush wwids file stream : %s", strerror(errno)); ret = -1; goto out; } ret = write_out_wwid(fd, wwid); out: fclose(f); return ret; } int should_multipath(struct path *pp1, vector pathvec) { int i; struct path *pp2; condlog(4, "checking if %s should be multipathed", pp1->dev); vector_foreach_slot(pathvec, pp2, i) { if (pp1->dev == pp2->dev) continue; if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) { condlog(3, "found multiple paths with wwid %s, " "multipathing %s", pp1->wwid, pp1->dev); return 1; } } if (check_wwids_file(pp1->wwid, 0) < 0) { condlog(3, "wwid %s not in wwids file, skipping %s", pp1->wwid, pp1->dev); return 0; } condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid, pp1->dev); return 1; } int remember_wwid(char *wwid) { int ret = check_wwids_file(wwid, 1); if (ret < 0){ condlog(3, "failed writing wwid %s to wwids file", wwid); return -1; } if (ret == 1) condlog(3, "wrote wwid %s to wwids file", wwid); else condlog(4, "wwid %s already in wwids file", wwid); return 0; } multipath-tools-0.5.0+git1.656f8865/libmultipath/wwids.h000066400000000000000000000010551260771327300225560ustar00rootroot00000000000000/* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ #ifndef _WWIDS_H #define _WWIDS_H #define WWIDS_FILE_HEADER \ "# Multipath wwids, Version : 1.0\n" \ "# NOTE: This file is automatically maintained by multipath and multipathd.\n" \ "# You should not need to edit this file in normal circumstances.\n" \ "#\n" \ "# Valid WWIDs:\n" int should_multipath(struct path *pp, vector pathvec); int remember_wwid(char *wwid); int check_wwids_file(char *wwid, int write_wwid); int remove_wwid(char *wwid); int replace_wwids(vector mp); #endif /* _WWIDS_H */ multipath-tools-0.5.0+git1.656f8865/mpathpersist/000077500000000000000000000000001260771327300212745ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/mpathpersist/Makefile000066400000000000000000000012121260771327300227300ustar00rootroot00000000000000# Makefile # include ../Makefile.inc OBJS = main.o CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev EXEC = mpathpersist all: $(EXEC) $(EXEC): $(OBJS) $(CC) -g $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: install -d $(DESTDIR)$(bindir) install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) clean: rm -f *.o $(EXEC) rm -f mpathpersist.8.gz uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) rm $(DESTDIR)$(mandir)/$(EXEC).8.gz multipath-tools-0.5.0+git1.656f8865/mpathpersist/main.c000066400000000000000000000531331260771327300223710ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include #include #include static const char * pr_type_strs[] = { "obsolete [0]", "Write Exclusive", "obsolete [2]", "Exclusive Access", "obsolete [4]", "Write Exclusive, registrants only", "Exclusive Access, registrants only", "Write Exclusive, all registrants", "Exclusive Access, all registrants", "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]", "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]", }; int get_transportids_length(unsigned char * transportid_arr, int max_transportid, int num_transportids); void mpath_print_buf_readcap(struct prin_resp *pr_buff); void mpath_print_buf_readfullstat(struct prin_resp *pr_buff); void mpath_print_buf_readresv(struct prin_resp *pr_buff); void mpath_print_buf_readkeys(struct prin_resp *pr_buff); void dumpHex(const char* str, int len, int no_ascii); void * mpath_alloc_prin_response(int prin_sa); void mpath_print_transport_id(struct prin_fulldescr *fdesc); int construct_transportid(const char * inp, struct transportid transid[], int num_transportids); int logsink; unsigned int mpath_mx_alloc_len; int main (int argc, char * argv[]) { int fd, c, res; const char *device_name = NULL; int num_prin_sa = 0; int num_prout_sa = 0; int num_prout_param = 0; int prin_flag = 0; int prout_flag = 0; int ret = 0; int hex = 0; uint64_t param_sark = 0; unsigned int prout_type = 0; int param_alltgpt = 0; int param_aptpl = 0; uint64_t param_rk = 0; unsigned int param_rtp = 0; int num_transportids = 0; struct transportid transportids[MPATH_MX_TIDS]; int prout = 1; int prin = 1; int prin_sa = -1; int prout_sa = -1; int verbose = 0; int loglevel = 0; int noisy = 0; int num_transport =0; void *resp = NULL; struct transportid * tmp; struct udev *udev = NULL; if (optind == argc) { fprintf (stderr, "No parameter used\n"); usage (); exit (1); } if (getuid () != 0) { fprintf (stderr, "need to be root\n"); exit (1); } udev = udev_new(); mpath_lib_init(udev); memset(transportids,0,MPATH_MX_TIDS); while (1) { int option_index = 0; c = getopt_long (argc, argv, "v:Cd:hHioZK:S:PAT:skrGILcRX:l:", long_options, &option_index); if (c == -1) break; switch (c) { case 'v': if (1 != sscanf (optarg, "%d", &loglevel)) { fprintf (stderr, "bad argument to '--verbose'\n"); return MPATH_PR_SYNTAX_ERROR; } break; case 'C': prout_sa = MPATH_PROUT_CLEAR_SA; ++num_prout_sa; break; case 'd': device_name = optarg; break; case 'h': usage (); return 0; case 'H': hex=1; break; case 'i': prin_flag = 1; break; case 'o': prout_flag = 1; break; case 'Z': param_aptpl = 1; ++num_prout_param; break; case 'K': if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_rk)) { fprintf (stderr, "bad argument to '--param-rk'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 'S': if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_sark)) { fprintf (stderr, "bad argument to '--param-sark'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 'P': prout_sa = MPATH_PROUT_PREE_SA; ++num_prout_sa; break; case 'A': prout_sa = MPATH_PROUT_PREE_AB_SA; ++num_prout_sa; break; case 'T': if (1 != sscanf (optarg, "%x", &prout_type)) { fprintf (stderr, "bad argument to '--prout-type'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 's': prin_sa = MPATH_PRIN_RFSTAT_SA; ++num_prin_sa; break; case 'k': prin_sa = MPATH_PRIN_RKEY_SA; ++num_prin_sa; break; case 'r': prin_sa = MPATH_PRIN_RRES_SA; ++num_prin_sa; break; case 'G': prout_sa = MPATH_PROUT_REG_SA; ++num_prout_sa; break; case 'I': prout_sa = MPATH_PROUT_REG_IGN_SA; ++num_prout_sa; break; case 'L': prout_sa = MPATH_PROUT_REL_SA; ++num_prout_sa; break; case 'c': prin_sa = MPATH_PRIN_RCAP_SA; ++num_prin_sa; break; case 'R': prout_sa = MPATH_PROUT_RES_SA; ++num_prout_sa; break; case 'X': if (0 != construct_transportid(optarg, transportids, num_transport)) { fprintf(stderr, "bad argument to '--transport-id'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_transport; break; case 'l': if (1 != sscanf(optarg, "%u", &mpath_mx_alloc_len)) { fprintf(stderr, "bad argument to '--alloc-length'\n"); return MPATH_PR_SYNTAX_ERROR; } else if (MPATH_MAX_PARAM_LEN < mpath_mx_alloc_len) { fprintf(stderr, "'--alloc-length' argument exceeds maximum" " limit(%d)\n", MPATH_MAX_PARAM_LEN); return MPATH_PR_SYNTAX_ERROR; } break; default: fprintf(stderr, "unrecognised switch " "code 0x%x ??\n", c); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } if (optind < argc) { if (NULL == device_name) { device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) fprintf (stderr, "Unexpected extra argument: %s\n", argv[optind]); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } /* set verbosity */ noisy = (loglevel >= 3) ? 1 : hex; verbose = (loglevel >= 3)? 3: loglevel; if ((prout_flag + prin_flag) == 0) { fprintf (stderr, "choose either '--in' or '--out' \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if ((prout_flag + prin_flag) > 1) { fprintf (stderr, "choose either '--in' or '--out' \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } else if (prout_flag) { /* syntax check on PROUT arguments */ prin = 0; if ((1 != num_prout_sa) || (0 != num_prin_sa)) { fprintf (stderr, " For Persistent Reserve Out only one " "appropriate\n service action must be " "chosen \n"); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } else if (prin_flag) { /* syntax check on PRIN arguments */ prout = 0; if (num_prout_sa > 0) { fprintf (stderr, " When a service action for Persistent " "Reserve Out is chosen the\n" " '--out' option must be given \n"); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if (0 == num_prin_sa) { fprintf (stderr, " No service action given for Persistent Reserve IN\n"); usage(); ret = MPATH_PR_SYNTAX_ERROR; } else if (num_prin_sa > 1) { fprintf (stderr, " Too many service actions given; choose " "one only\n"); usage(); ret = MPATH_PR_SYNTAX_ERROR; } } else { usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if ((param_rtp) && (MPATH_PROUT_REG_MOV_SA != prout_sa)) { fprintf (stderr, " --relative-target-port" " only useful with --register-move\n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if (((MPATH_PROUT_RES_SA == prout_sa) || (MPATH_PROUT_REL_SA == prout_sa) || (MPATH_PROUT_PREE_SA == prout_sa) || (MPATH_PROUT_PREE_AB_SA == prout_sa)) && (0 == prout_type)) { fprintf(stderr, "Warning: --prout-type probably needs to be " "given\n"); } if ((verbose > 2) && num_transportids) { fprintf (stderr, "number of tranport-ids decoded from " "command line : %d\n", num_transportids); } if (device_name == NULL) { fprintf (stderr, "No device name given \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } /* open device */ if ((fd = open (device_name, O_WRONLY)) < 0) { fprintf (stderr, "%s: error opening file (rw) fd=%d\n", device_name, fd); ret = MPATH_PR_FILE_ERROR; goto out; } if (prin) { resp = mpath_alloc_prin_response(prin_sa); if (!resp) { fprintf (stderr, "failed to allocate PRIN response buffer\n"); ret = MPATH_PR_OTHER; goto out; } ret = mpath_persistent_reserve_in (fd, prin_sa, resp, noisy, verbose); if (ret != MPATH_PR_SUCCESS ) { fprintf (stderr, "Persistent Reserve IN command failed\n"); goto out; } switch(prin_sa) { case MPATH_PRIN_RKEY_SA: mpath_print_buf_readkeys(resp); break; case MPATH_PRIN_RRES_SA: mpath_print_buf_readresv(resp); break; case MPATH_PRIN_RCAP_SA: mpath_print_buf_readcap(resp); break; case MPATH_PRIN_RFSTAT_SA: mpath_print_buf_readfullstat(resp); break; } free(resp); } else if (prout) { int j; struct prout_param_descriptor *paramp; paramp= malloc(sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS ))); memset(paramp, 0, sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS))); for (j = 7; j >= 0; --j) { paramp->key[j] = (param_rk & 0xff); param_rk >>= 8; } for (j = 7; j >= 0; --j) { paramp->sa_key[j] = (param_sark & 0xff); param_sark >>= 8; } if (param_alltgpt) paramp->sa_flags |= 0x4; if (param_aptpl) paramp->sa_flags |= 0x1; if (num_transport) { paramp->sa_flags |= MPATH_F_SPEC_I_PT_MASK; paramp->num_transportid = num_transport; for (j = 0 ; j < num_transport; j++) { paramp->trnptid_list[j] = (struct transportid *)malloc(sizeof(struct transportid)); memcpy(paramp->trnptid_list[j], &transportids[j],sizeof(struct transportid)); } } /* PROUT commands other than 'register and move' */ ret = mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type, paramp, noisy, verbose); for (j = 0 ; j < num_transport; j++) { tmp = paramp->trnptid_list[j]; free(tmp); } free(paramp); } if (ret != MPATH_PR_SUCCESS) { switch(ret) { case MPATH_PR_SENSE_UNIT_ATTENTION: printf("persistent reserve out: scsi status: Unit Attention\n"); break; case MPATH_PR_RESERV_CONFLICT: printf("persistent reserve out: scsi status: Reservation Conflict\n"); break; } printf("PR out: command failed\n"); } res = close (fd); if (res < 0) { mpath_lib_exit(); udev_unref(udev); return MPATH_PR_FILE_ERROR; } out : mpath_lib_exit(); udev_unref(udev); return (ret >= 0) ? ret : MPATH_PR_OTHER; } int get_transportids_length(unsigned char * transportid_arr, int max_transportid, int num_transportids) { int compact_len = 0; unsigned char * ucp = transportid_arr; int k, off, protocol_id, len; for (k = 0, off = 0; ((k < num_transportids) && (k < max_transportid)); ++k, off += MPATH_MX_TID_LEN) { protocol_id = ucp[off] & 0xf; if (5 == protocol_id) { len = (ucp[off + 2] << 8) + ucp[off + 3] + 4; if (len < 24) len = 24; if (off > compact_len) memmove(ucp + compact_len, ucp + off, len); compact_len += len; } else { if (off > compact_len) memmove(ucp + compact_len, ucp + off, 24); compact_len += 24; } } return compact_len; } void mpath_print_buf_readkeys( struct prin_resp *pr_buff) { int i,j,k, num; unsigned char *keyp; uint64_t prkey; printf(" PR generation=0x%x, ", pr_buff->prin_descriptor.prin_readkeys.prgeneration); num = pr_buff->prin_descriptor.prin_readkeys.additional_length / 8; if (0 == num) { printf(" 0 registered reservation key.\n"); return; } else if (1 == num) printf(" 1 registered reservation key follows:\n"); else printf(" %d registered reservation keys follow:\n", num); keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[0]; for (i = 0; i < num ; i++) { prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= keyp[j]; } printf(" 0x%" PRIx64 "\n", prkey); k=8*i+j; keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[k]; } } void mpath_print_buf_readresv( struct prin_resp *pr_buff) { int j, num, scope=0, type=0; unsigned char *keyp; uint64_t prkey; num = pr_buff->prin_descriptor.prin_readresv.additional_length / 8; if (0 == num) { printf(" PR generation=0x%x, there is NO reservation held \n", pr_buff->prin_descriptor.prin_readresv.prgeneration); return ; } else printf(" PR generation=0x%x, Reservation follows:\n", pr_buff->prin_descriptor.prin_readresv.prgeneration); keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[0]; prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= keyp[j]; } printf(" Key = 0x%" PRIx64 "\n", prkey); scope = (pr_buff->prin_descriptor.prin_readresv.scope_type >> 4) & 0x0f; type = pr_buff->prin_descriptor.prin_readresv.scope_type & 0x0f; if (scope == 0) printf(" scope = LU_SCOPE, type = %s", pr_type_strs[type]); else printf(" scope = %d, type = %s", scope, pr_type_strs[type]); printf("\n"); } void mpath_print_buf_readcap( struct prin_resp *pr_buff) { if ( pr_buff->prin_descriptor.prin_readcap.length <= 2 ) { fprintf(stderr, "Unexpected response for PRIN Report " "Capabilities\n"); return; //MALFORMED; } printf("Report capabilities response:\n"); printf(" Compatible Reservation Handling(CRH): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); printf(" Specify Initiator Ports Capable(SIP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); printf(" All Target Ports Capable(ATP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4 )); printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0])); printf(" Type Mask Valid(TMV): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); printf(" Allow Commands: %d\n", !!(( pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); printf(" Persist Through Power Loss Active(PTPL_A): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); if(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80) { printf(" Support indicated in Type mask:\n"); printf(" %s: %d\n", pr_type_strs[7], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x80); printf(" %s: %d\n", pr_type_strs[6], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x40); printf(" %s: %d\n", pr_type_strs[5], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x20); printf(" %s: %d\n", pr_type_strs[3], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x8); printf(" %s: %d\n", pr_type_strs[1], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x2); printf(" %s: %d\n", pr_type_strs[8], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x100); } } void mpath_print_buf_readfullstat( struct prin_resp *pr_buff) { int i,j, num; uint64_t prkey; uint16_t rel_pt_addr; unsigned char * keyp; num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; if (0 == num) { printf(" PR generation=0x%x \n", pr_buff->prin_descriptor.prin_readfd.prgeneration); return ; } else printf(" PR generation=0x%x \n", pr_buff->prin_descriptor.prin_readfd.prgeneration); for (i = 0 ; i < num; i++) { keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key; prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= *keyp; ++keyp; } printf(" Key = 0x%" PRIx64 "\n", prkey); if (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->flag & 0x02) printf(" All target ports bit set\n"); else { printf(" All target ports bit clear\n"); rel_pt_addr = pr_buff->prin_descriptor.prin_readfd.descriptors[i]->rtpi; printf(" Relative port address: 0x%x\n", rel_pt_addr); } if (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->flag & 0x1) { printf(" << Reservation holder >>\n"); j = ((pr_buff->prin_descriptor.prin_readfd.descriptors[i]->scope_type>> 4) & 0xf); if (0 == j) printf(" scope: LU_SCOPE, "); else printf(" scope: %d ", j); j = (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->scope_type & 0xf); printf(" type: %s\n", pr_type_strs[j]); } else printf(" not reservation holder\n"); mpath_print_transport_id(pr_buff->prin_descriptor.prin_readfd.descriptors[i]); } } static void usage() { fprintf(stderr, "Usage: mpathpersist [OPTIONS] [DEVICE]\n" " Options:\n" " --verbose|-v level verbosity level\n" " 0 Critical messages\n" " 1 Error messages\n" " 2 Warning messages\n" " 3 Informational messages\n" " 4 Informational messages with trace enabled\n" " --clear|-C PR Out: Clear\n" " --device=DEVICE|-d DEVICE query or change DEVICE\n" " --help|-h output this usage message\n" " --hex|-H output response in hex\n" " --in|-i request PR In command \n" " --out|-o request PR Out command\n" " --param-aptpl|-Z PR Out parameter 'APTPL'\n" " --read-keys|-k PR In: Read Keys\n" " --param-sark=SARK|-S SARK PR Out parameter service " "action\n" " reservation key (SARK is in " "hex)\n" " --preempt|-P PR Out: Preempt\n" " --preempt-abort|-A PR Out: Preempt and Abort\n" " --prout-type=TYPE|-T TYPE PR Out command type\n" " --read-full-status|-s PR In: Read Full Status\n" " --read-keys|-k PR In: Read Keys\n" " --read-reservation|-r PR In: Read Reservation\n" " --register|-G PR Out: Register\n" " --register-ignore|-I PR Out: Register and Ignore\n" " --release|-L PR Out: Release\n" " --report-capabilities|-c PR In: Report Capabilities\n" " --reserve|-R PR Out: Reserve\n" " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned \n" " in several forms\n" " Examples:\n" " mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n" " mpathpersist -i -k /dev/mapper/mpath9\n" ); } void mpath_print_transport_id(struct prin_fulldescr *fdesc) { switch (fdesc->trnptid.protocol_id) { case MPATH_PROTOCOL_ID_FC: printf(" FCP-2 "); if (0 != fdesc->trnptid.format_code) printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.n_port_name, 8, 0); break; case MPATH_PROTOCOL_ID_ISCSI: printf(" iSCSI "); if (0 == fdesc->trnptid.format_code) { printf("name: %.*s\n", (int)sizeof(fdesc->trnptid.iscsi_name), fdesc->trnptid.iscsi_name); }else if (1 == fdesc->trnptid.format_code){ printf("world wide unique port id: %.*s\n", (int)sizeof(fdesc->trnptid.iscsi_name), fdesc->trnptid.iscsi_name); }else { printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.iscsi_name, (int)sizeof(fdesc->trnptid.iscsi_name), 0); } break; case MPATH_PROTOCOL_ID_SAS: printf(" SAS "); if (0 != fdesc->trnptid.format_code) printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.sas_address, 8, 0); break; default: return; } } int construct_transportid(const char * lcp, struct transportid transid[], int num_transportids) { int k = 0; int j, n, b, c, len, alen; const char * ecp; const char * isip; if ((0 == memcmp("fcp,", lcp, 4)) || (0 == memcmp("FCP,", lcp, 4))) { lcp += 4; k = strspn(lcp, "0123456789aAbBcCdDeEfF"); len = strlen(lcp); if (len != k) { fprintf(stderr, "badly formed symbolic FCP TransportID: %s\n", lcp); return 1; } transid[num_transportids].format_code = MPATH_PROTOCOL_ID_FC; transid[num_transportids].protocol_id = MPATH_WWUI_DEVICE_NAME; for (k = 0, j = 0, b = 0; k < 16; ++k) { c = lcp[k]; if (isdigit(c)) n = c - 0x30; else if (isupper(c)) n = c - 0x37; else n = c - 0x57; if (k & 1) { transid[num_transportids].n_port_name[j] = b | n; ++j; } else b = n << 4; } goto my_cont_b; } if ((0 == memcmp("sas,", lcp, 4)) || (0 == memcmp("SAS,", lcp, 4))) { lcp += 4; k = strspn(lcp, "0123456789aAbBcCdDeEfF"); len =strlen(lcp); if (len != k) { fprintf(stderr, "badly formed symbolic SAS TransportID: %s\n", lcp); return 1; } transid[num_transportids].format_code = MPATH_PROTOCOL_ID_SAS; transid[num_transportids].protocol_id = MPATH_WWUI_DEVICE_NAME; memcpy(&transid[num_transportids].sas_address, lcp, 8); goto my_cont_b; } if (0 == memcmp("iqn.", lcp, 4)) { ecp = strpbrk(lcp, " \t"); isip = strstr(lcp, ",i,0x"); if (ecp && (isip > ecp)) isip = NULL; len = ecp ? (ecp - lcp) : (int)strlen(lcp); transid[num_transportids].format_code = (isip ? MPATH_WWUI_PORT_IDENTIFIER:MPATH_WWUI_DEVICE_NAME); transid[num_transportids].protocol_id = MPATH_PROTOCOL_ID_ISCSI; alen = len + 1; /* at least one trailing null */ if (alen < 20) alen = 20; else if (0 != (alen % 4)) alen = ((alen / 4) + 1) * 4; if (alen > 241) { /* sam5r02.pdf A.2 (Annex) */ fprintf(stderr, "iSCSI name too long, alen=%d\n", alen); return 0; } transid[num_transportids].iscsi_name[1] = alen & 0xff; memcpy(&transid[num_transportids].iscsi_name[2], lcp, len); goto my_cont_b; } my_cont_b: if (k >= MPATH_MAX_PARAM_LEN) { fprintf(stderr, "build_transportid: array length exceeded\n"); return 1; } return 0; } multipath-tools-0.5.0+git1.656f8865/mpathpersist/main.h000066400000000000000000000013061260771327300223710ustar00rootroot00000000000000static struct option long_options[] = { {"verbose", 1, 0, 'v'}, {"clear", 0, 0, 'C'}, {"device", 1, 0, 'd'}, {"help", 0, 0, 'h'}, {"hex", 0, 0, 'H'}, {"in", 0, 0, 'i'}, {"out", 0, 0, 'o'}, {"param-aptpl", 0, 0, 'Z'}, {"param-rk", 1, 0, 'K'}, {"param-sark", 1, 0, 'S'}, {"preempt", 0, 0, 'P'}, {"preempt-abort", 0, 0, 'A'}, {"prout-type", 1, 0, 'T'}, {"read-full-status", 0, 0, 's'}, {"read-keys", 0, 0, 'k'}, {"read-reservation", 0, 0, 'r'}, {"register", 0, 0, 'G'}, {"register-ignore", 0, 0, 'I'}, {"release", 0, 0, 'L'}, {"report-capabilities", 0, 0, 'c'}, {"reserve", 0, 0, 'R'}, {"transport-id", 1, 0, 'X'}, {"alloc-length", 1, 0, 'l'}, {0, 0, 0, 0} }; static void usage(void); multipath-tools-0.5.0+git1.656f8865/mpathpersist/mpathpersist.8000066400000000000000000000034231260771327300241120ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.39.2. .TH MPATHPERSIST "8" "April 2011" "mpathpersist" "User Commands" .SH NAME mpathpersist .SH SYNOPSIS .B mpathpersist [\fIOPTIONS\fR] [\fIDEVICE\fR] .SH DESCRIPTION .IP Options: .TP \fB\-\-verbose\fR|\-v level verbosity level .TP 0 Critical and error messages .TP 1 Warning messages .TP 2 Informational messages .TP 3 Informational messages with trace enabled .TP \fB\-\-clear\fR|\-C PR Out: Clear .TP \fB\-\-device\fR=\fIDEVICE\fR|\-d DEVICE query or change DEVICE .TP \fB\-\-help\fR|\-h output this usage message .TP \fB\-\-hex\fR|\-H output response in hex .TP \fB\-\-in\fR|\-i request PR In command .TP \fB\-\-out\fR|\-o request PR Out command .TP \fB\-\-param\-aptpl\fR|\-Z PR Out parameter 'APTPL' .TP \fB\-\-read\-keys\fR|\-k PR In: Read Keys .TP \fB\-\-param\-sark\fR=\fISARK\fR|\-S SARK PR Out parameter service action reservation key (SARK is in hex) .TP \fB\-\-preempt\fR|\-P PR Out: Preempt .TP \fB\-\-preempt\-abort\fR|\-A PR Out: Preempt and Abort .TP \fB\-\-prout\-type\fR=\fITYPE\fR|\-T TYPE PR Out command type .TP \fB\-\-read\-status\fR|\-s PR In: Read Full Status .TP \fB\-\-read\-keys\fR|\-k PR In: Read Keys .TP \fB\-\-read\-reservation\fR|\-r PR In: Read Reservation .TP \fB\-\-register\fR|\-G PR Out: Register .TP \fB\-\-register\-ignore\fR|\-I PR Out: Register and Ignore .TP \fB\-\-release\fR|\-L PR Out: Release .TP \fB\-\-report\-capabilities\fR|\-c PR In: Report Capabilities .TP \fB\-\-reserve\fR|\-R PR Out: Reserve .TP \fB\-\-transport\-id\fR=\fITIDS\fR|\-X TIDS TransportIDs can be mentioned in several forms .IP Examples: .IP mpathpersist \fB\-\-out\fR \fB\-\-register\fR \fB\-\-param\-sark\fR=\fI123abc\fR \fB\-\-prout\-type\fR=\fI5\fR /dev/mapper/mpath9 mpathpersist \fB\-i\fR \fB\-k\fR /dev/mapper/mpath9 .PP multipath-tools-0.5.0+git1.656f8865/multipath-tools.spec.in000066400000000000000000000030211260771327300231730ustar00rootroot00000000000000%define _rpmdir rpms %define _builddir . Summary: Tools to manage multipathed devices with the device-mapper. Name: multipath-tools Version: __VERSION__ Release: 1 License: GPL Group: Utilities/System URL: http://christophe.varoqui.free.fr Source: /dev/null BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Packager: Christophe Varoqui Prefix: / Vendor: Starving Linux Artists (tm Brian O'Sullivan) ExclusiveOS: linux %description %{name} provides the tools to manage multipathed devices by instructing the device-mapper multipath module what to do. The tools are : * multipath : scan the system for multipathed devices, assembles them and update the device-mapper's maps * multipathd : wait for maps events, then execs multipath * kpartx : maps linear devmaps upon device partitions, which makes multipath maps partionable %prep mkdir -p %{buildroot} %{_rpmdir} %build make %install rm -rf %{buildroot} make DESTDIR=%{buildroot} install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{prefix}/sbin/multipath %{prefix}/sbin/kpartx %{prefix}/usr/share/man/man8/multipath.8.gz %{prefix}/usr/share/man/man8/kpartx.8.gz %{prefix}/usr/share/man/man8/multipathd.8.gz %{prefix}/usr/share/man/man5/multipath.conf.5.gz %{prefix}/sbin/multipathd %{prefix}/etc/udev/rules.d/multipath.rules %{prefix}/etc/udev/rules.d/kpartx.rules %{prefix}/lib/udev/kpartx_id %{prefix}/lib/multipath/*.so %changelog * Sat May 14 2004 Christophe Varoqui - Initial build. multipath-tools-0.5.0+git1.656f8865/multipath.conf.annotated000066400000000000000000000567141260771327300234200ustar00rootroot00000000000000## ## This is a template multipath-tools configuration file ## Uncomment the lines relevent to your environment ## # ## ## name : defaults ## desc : multipath-tools default settings ## #defaults { # # # # name : polling_interval # # scope : multipathd # # desc : interval between two path checks in seconds. For # # properly functioning paths, the interval between checks # # will gradually increase to (4 * polling_interval). # # values : n > 0 # # default : 5 # # # polling_interval 10 # # # # # name : path_selector # # scope : multipath & multipathd # # desc : the default path selector algorithm to use # # these algorithms are offered by the kernel multipath target # # values : "round-robin 0" = Loop through every path in the path group, # # sending the same amount of IO to each. # # "queue-length 0" = Send the next bunch of IO down the path # # with the least amount of outstanding IO. # # "service-time 0" = Choose the path for the next bunch of IO # # based on the amount of outstanding IO to # # the path and its relative throughput. # # default : "service-time 0" # # # path_selector "service-time 0" # # # # # name : path_grouping_policy # # scope : multipath & multipathd # # desc : the default path grouping policy to apply to unspecified # # multipaths # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority group # # group_by_serial = 1 priority group per detected serial # # number # # group_by_prio = 1 priority group per path priority # # value # # group_by_node_name = 1 priority group per target node name # # default : failover # # # path_grouping_policy multibus # # # # # name : uid_attribute # # scope : multipath & multipathd # # desc : the default udev attribute from which the path # # identifier should be generated. # # default : ID_SERIAL # # # uid_attribute "ID_SERIAL" # # # # # name : getuid_callout # # scope : multipath & multipathd # # desc : the default program and args to callout to obtain a unique # # path identifier. This parameter is deprecated. # # This parameter is deprecated, superseded by uid_attribute # # default : /lib/udev/scsi_id --whitelisted --device=/dev/%n # # # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # # # # # name : prio # # scope : multipath & multipathd # # desc : the default function to call to obtain a path # # priority value. The ALUA bits in SPC-3 provide an # # exploitable prio value for example. # # default : const # # # prio "alua" # # # # # name : prio_args # # scope : multipath & multipathd # # desc : The arguments string passed to the prio function # # Most prio functions do not need arguments. The # # datacore prioritizer need one. # # default : (null) # # # prio_args "timeout=1000 preferredsds=foo" # # # # # name : features # # scope : multipath & multipathd # # desc : The default extra features of multipath devices. # # Syntax is "num[ feature_0 feature_1 ...]", where `num' is the # # number of features in the following (possibly empty) list of # # features. # # values : queue_if_no_path = Queue IO if no path is active; consider # # using the `no_path_retry' keyword instead. # # no_partitions = Disable automatic partitions generation via # # kpartx. # # default : "0" # # # features "0" # features "1 queue_if_no_path" # features "1 no_partitions" # features "2 queue_if_no_path no_partitions" # # # # # name : path_checker, checker # # scope : multipath & multipathd # # desc : the default method used to determine the paths' state # # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac|cciss_tur # # default : directio # # # path_checker directio # # # # # name : rr_min_io # # scope : multipath & multipathd # # desc : the number of IO to route to a path before switching # # to the next in the same path group for the bio-based # # multipath implementation. This parameter is used for # # kernels version up to 2.6.31; newer kernel version # # use the parameter rr_min_io_rq # # default : 1000 # # # rr_min_io 1000 # # # # # name : rr_min_io_rq # # scope : multipath & multipathd # # desc : the number of IO to route to a path before switching # # to the next in the same path group for the request-based # # multipath implementation. This parameter is used for # # kernels versions later than 2.6.31. # # default : 1 # # # rr_min_io_rq 1 # # # # # name : flush_on_last_del # # scope : multipathd # # desc : If set to "yes", multipathd will disable queueing when the # # last path to a device has been deleted. # # values : yes|no # # default : no # # # flush_on_last_del yes # # # # # name : max_fds # # scope : multipathd # # desc : Sets the maximum number of open file descriptors for the # # multipathd process. # # values : max|n > 0 # # default : None # # # max_fds 8192 # # # # # name : rr_weight # # scope : multipath & multipathd # # desc : if set to priorities the multipath configurator will assign # # path weights as "path prio * rr_min_io" # # values : priorities|uniform # # default : uniform # # # rr_weight priorities # # # # # name : failback # # scope : multipathd # # desc : tell the daemon to manage path group failback, or not to. # # 0 means immediate failback, values >0 means deffered # # failback expressed in seconds. # # values : manual|immediate|n > 0 # # default : manual # # # failback immediate # # # # # name : no_path_retry # # scope : multipath & multipathd # # desc : tell the number of retries until disable queueing, or # # "fail" means immediate failure (no queueing), # # "queue" means never stop queueing # # values : queue|fail|n (>0) # # default : (null) # # # no_path_retry queue # # # # # name : queue_without_daemon # # scope : multipathd # # desc : If set to "no", multipathd will disable queueing for all # # devices when it is shut down. # # values : yes|no # # default : yes # queue_without_daemon no # # # # # name : user_friendly_names # # scope : multipath & multipathd # # desc : If set to "yes", using the bindings file # # /etc/multipath/bindings to assign a persistent and # # unique alias to the multipath, in the form of mpath. # # If set to "no" use the WWID as the alias. In either case # # this be will be overriden by any specific aliases in this # # file. # # values : yes|no # # default : no # user_friendly_names no # # # # # name : mode # # scope : multipath & multipathd # # desc : The mode to use for the multipath device nodes, in octal. # # values : 0000 - 0777 # # default : determined by the process # mode 0644 # # # # # name : uid # # scope : multipath & multipathd # # desc : The user id to use for the multipath device nodes. You # # may use either the numeric or symbolic uid # # values : # # default : determined by the process # uid 0 # # # # # name : gid # # scope : multipath & multipathd # # desc : The group id to user for the multipath device nodes. You # # may use either the numeric or symbolic gid # # values : # # default : determined by the process # gid disk # # # # # name : checker_timeout # # scope : multipath & multipathd # # desc : The timeout to use for path checkers and prioritizers # # that issue scsi commands with an explicit timeout, in # # seconds. # # values : n > 0 # # default : taken from /sys/block/sd/device/timeout # checker_timeout 60 # # # # # name : fast_io_fail_tmo # # scope : multipath & multipathd # # desc : The number of seconds the scsi layer will wait after a # # problem has been detected on a FC remote port before failing # # IO to devices on that remote port. # # values : off | n >= 0 (smaller than dev_loss_tmo) # # default : determined by the OS # fast_io_fail_tmo 5 # # # # # name : dev_loss_tmo # # scope : multipath & multipathd # # desc : The number of seconds the scsi layer will wait after a # # problem has been detected on a FC remote port before # # removing it from the system. # # values : infinity | n > 0 # # default : determined by the OS # dev_loss_tmo 600 # # # # # name : bindings_file # # scope : multipath # # desc : The location of the bindings file that is used with # # the user_friendly_names option. # # values : # # default : "/var/lib/multipath/bindings" # bindings_file "/etc/multipath/bindings" # # # # # name : wwids_file # # scope : multipath # # desc : The location of the wwids file multipath uses to # # keep track of the created multipath devices. # # values : # # default : "/var/lib/multipath/wwids" # wwids_file "/etc/multipath/wwids" # # # # # name : reservation_key # # scope : multipath # # desc : Service action reservation key used by mpathpersist. # # values : # # default : (null) # reservation_key "mpathkey" # # # # # name : force_sync # # scope : multipathd # # desc : If set to yes, multipath will run all of the checkers in # # sync mode, even if the checker has an async mode. # # values : yes|no # # default : no # force_sync yes # # # # # name : config_dir # # scope : multipath & multipathd # # desc : If not set to an empty string, multipath will search # # this directory alphabetically for files ending in ".conf" # # and it will read configuration information from these # # files, just as if it was in /etc/multipath.conf # # values : "" or a fully qualified pathname # # default : "/etc/multipath/conf.d" # # # # # name : delay_watch_checks # # scope : multipathd # # desc : If set to a value greater than 0, multipathd will watch # # paths that have recently become valid for this many # # checks. If they fail again while they are being watched, # # when they next become valid, they will not be used until # # they have stayed up for delay_wait_checks checks. # # values : no| > 0 # # default : no # delay_watch_checks 12 # # # # # name : delay_wait_checks # # scope : multipathd # # desc : If set to a value greater than 0, when a device that has # # recently come back online fails again within # # delay_watch_checks checks, the next time it comes back # # online, it will marked and delayed, and not used until # # it has passed delay_wait_checks checks. # # values : no| > 0 # # default : no # delay_wait_checks 12 #} # ## ## name : blacklist ## scope : multipath & multipathd ## desc : list of device names to discard as not multipath candidates ## Devices can be identified by their device node name "devnode", ## their WWID "wwid", or their vender and product strings ## "device" ## default : fd, hd, md, dm, sr, scd, st, ram, raw, loop, dcssblk ## #blacklist { # wwid 26353900f02796769 # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^hd[a-z]" # devnode "^dcssblk[0-9]*" # device { # vendor DEC.* # product MSA[15]00 # } #} ## ## name : blacklist_exceptions ## scope : multipath & multipathd ## desc : list of device names to be treated as multipath candidates ## even if they are on the blacklist. ## Note: blacklist exceptions are only valid in the same class. ## It is not possible to blacklist devices using the devnode keyword ## and to exclude some devices of them using the wwid keyword. ## default : - ## #blacklist_exceptions { # devnode "^dasd[c-d]+[0-9]*" # wwid "IBM.75000000092461.4d00.34" # wwid "IBM.75000000092461.4d00.35" # wwid "IBM.75000000092461.4d00.36" #} # ## ## name : multipaths ## scope : multipath & multipathd ## desc : list of multipaths finest-grained settings ## #multipaths { # # # # name : multipath # # scope : multipath & multipathd # # desc : container for settings that apply to one specific multipath # # # multipath { # # # # name : wwid # # scope : multipath & multipathd # # desc : index of the container # # # wwid 3600508b4000156d700012000000b0000 # # # # # name : alias # # scope : multipath & multipathd # # desc : symbolic name for the multipath. If you are using # # user_friendly_names, do not set the alias to # # mpath. This may conflict with an automatically # # assigned user friendly name, and give you # # incorrect device node names. # # # alias yellow # # # # # name : path_grouping_policy # # scope : multipath & multipathd # # desc : path grouping policy to apply to this multipath # # values : failover, multibus, group_by_serial # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority # # group # # group_by_serial = 1 priority group per detected # # serial number # # group_by_prio = 1 priority group per path # # priority value # # group_by_node_name = 1 priority group per target # # node name # # # path_grouping_policy failover # # # # # name : path_selector # # scope : multipath & multipathd # # desc : the path selector algorithm to use for this mpath # # these algo are offered by the kernel mpath target # # values : "round-robin 0" # # # path_selector "round-robin 0" # # # # # name : failback # # scope : multipathd # # desc : tell the daemon to manage path group failback, or # # not to. 0 means immediate failback, values >0 means # # deffered failback expressed in seconds. # # values : manual|immediate|n > 0 # # # failback manual # # # # # name : rr_weight # # scope : multipath & multipathd # # desc : if set to priorities the multipath configurator will # # assign path weights as "path prio * rr_min_io" # # values : priorities|uniform # # # rr_weight priorities # # # # # name : no_path_retry # # scope : multipath & multipathd # # desc : tell the number of retries until disable queueing, # # or "fail" means immediate failure (no queueing), # # "queue" means never stop queueing # # values : queue|fail|n (>0) # # # no_path_retry queue # # # # # name : rr_min_io # # scope : multipath & multipathd # # desc : the number of IO to route to a path before switching # # to the next in the same path group # # # rr_min_io 100 # # # # # name : flush_on_last_del # # scope : multipathd # # desc : If set to "yes", multipathd will disable queueing # # when the last path to a device has been deleted. # # values : yes|no # # default : no # # # flush_on_last_del yes # # # # # name : mode # # scope : multipath & multipathd # # desc : The mode to use for the multipath device nodes, in # # octal. # # values : 0000 - 0777 # # default : determined by the process # mode 0644 # # # # # name : uid # # scope : multipath & multipathd # # desc : The user id to use for the multipath device nodes. # # You may use either the numeric or symbolic uid # # values : # # default : determined by the process # uid 0 # # # # # name : gid # # scope : multipath & multipathd # # desc : The group id to user for the multipath device nodes. # # You may use either the numeric or symbolic gid # # values : # # default : determined by the process # gid 0 # # # # # name : delay_watch_checks # # scope : multipathd # # desc : If set to a value greater than 0, multipathd will # # watch paths that have recently become valid for # # this many checks. If they fail again while they # # are being watched, when they next become valid, # # they will not be used until they have stayed up for # # delay_wait_checks checks. # # values : no| > 0 # delay_watch_checks 12 # # # # # name : delay_wait_checks # # scope : multipathd # # desc : If set to a value greater than 0, when a device # # that has recently come back online fails again # # within delay_watch_checks checks, the next time it # # comes online, it will marked and delayed, and not # # used until it has passed delay_wait_checks checks. # # values : no| > 0 # delay_wait_checks 12 # } # multipath { # wwid 1DEC_____321816758474 # alias red # rr_weight priorities # } #} # ## ## name : devices ## scope : multipath & multipathd ## desc : list of per storage controller settings ## overrides default settings (device_maps block) ## overriden by per multipath settings (multipaths block) ## and the overrides settings (overrides block) ## #devices { # # # # name : device # # scope : multipath & multipathd # # desc : settings for this specific storage controller # # # device { # # # # name : vendor, product # # scope : multipath & multipathd # # desc : index for the block # # # vendor "COMPAQ " # product "HSV110 (C)COMPAQ" # # # # # name : path_grouping_policy # # scope : multipath & multipathd # # desc : path grouping policy to apply to this multipath # # values : failover, multibus, group_by_serial # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority # # group # # group_by_serial = 1 priority group per detected # # serial number # # group_by_prio = 1 priority group per path # # priority value # # group_by_node_name = 1 priority group per target # # node name # # # path_grouping_policy failover # # # # # name : getuid_callout # # scope : multipath & multipathd # # desc : the program and args to callout to obtain a unique # # path identifier. Absolute path required # # # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # # # # # name : prio # # scope : multipath & multipathd # # desc : the function to call to obtain a path # # weight. Weights are summed for each path group to # # determine the next PG to use case of failure. # # default : no callout, all paths equals # # # prio "hp_sw" # # # # # name : prio_args # # scope : multipath & multipathd # # desc : The arguments string passed to the prio function # # Most prio functions do not need arguments. The # # datacore prioritizer need one. # # default : (null) # # # prio_args "timeout=1000 preferredsds=foo" # # # name : path_checker, checker # # scope : multipathd & multipathd # # desc : path checking algorithm to use to check path state # # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac| # # cciss_tur # # # path_checker directio # # # as already described # path_selector "service-time 0" # # # as already described # features "0" # # # # # name : hardware_handler # # scope : multipath & multipathd # # desc : If set, it specifies a module that will be used to # # perform hardware specific actions when switching # # path groups or handling IO errors # # values : "0"|"1 emc" # # default : "0" # # # hardware_handler "1 emc" # # # # # name : failback # # scope : multipathd # # desc : tell the daemon to manage path group failback, or # # not to. 0 means immediate failback, values >0 means # # deffered failback expressed in seconds. # # values : manual|immediate|n > 0 # # # failback 30 # # # # # name : rr_weight # # scope : multipath & multipathd # # desc : if set to priorities the multipath configurator will # # assign path weights as "path prio * rr_min_io" # # values : priorities|uniform # # # rr_weight priorities # # # # # name : no_path_retry # # scope : multipath & multipathd # # desc : tell the number of retries until disable queueing, # # or "fail" means immediate failure (no queueing), # # "queue" means never stop queueing # # values : queue|fail|n (>0) # # # no_path_retry queue # # # # # name : rr_min_io # # scope : multipath & multipathd # # desc : the number of IO to route to a path before switching # # to the next in the same path group # # # rr_min_io 100 # # # # # name : flush_on_last_del # # scope : multipathd # # desc : If set to "yes", multipathd will disable queueing # # when the last path to a device has been deleted. # # values : yes|no # # # flush_on_last_del yes # # # # # name : product_blacklist # # scope : multipath & multipathd # # desc : product strings to blacklist for this vendor # # # product_blacklist LUN_Z # # # # # name : fast_io_fail_tmo # # scope : multipath & multipathd # # desc : The number of seconds the scsi layer will wait after # # a problem has been detected on a FC remote port # # before failing IO to devices on that remote port. # # values : off | n >= 0 (smaller than dev_loss_tmo) # fast_io_fail_tmo 5 # # # # # name : dev_loss_tmo # # scope : multipath & multipathd # # desc : The number of seconds the scsi layer will wait after # # a problem has been detected on a FC remote port # # before removing it from the system. # # values : n > 0 # dev_loss_tmo 600 # # # # # name : delay_watch_checks # # scope : multipathd # # desc : If set to a value greater than 0, multipathd will # # watch paths that have recently become valid for # # this many checks. If they fail again while they # # are being watched, when they next become valid, # # they will not be used until they have stayed up for # # delay_wait_checks checks. # # values : no| > 0 # delay_watch_checks 12 # # # # # name : delay_wait_checks # # scope : multipathd # # desc : If set to a value greater than 0, when a device # # that has recently come back online fails again # # within delay_watch_checks checks, the next time it # # comes online, it will marked and delayed, and not # # used until it has passed delay_wait_checks checks. # # values : no| > 0 # delay_wait_checks 12 # # } # device { # vendor "COMPAQ " # product "MSA1000 " # path_grouping_policy multibus # path_checker tur # rr_weight priorities # } #} # ## ## name : devices ## scope : multipath & multipathd ## desc : list of settings to override all hadware settings for all devices ## overrides default settings (device_maps block) ## and per device type settings (devices block) ## overriden by per multipath settings (multipaths block) ## # attributes and values are identical to the device block # #overrides { # dev_loss_tmo 60 # no_path_retry fail #} multipath-tools-0.5.0+git1.656f8865/multipath.conf.defaults000066400000000000000000000472431260771327300232470ustar00rootroot00000000000000# These are the compiled in default settings. They will be used unless you # overwrite these values in your config file. #defaults { # verbosity 2 # polling_interval 5 # path_selector "service-time 0" # path_grouping_policy "failover" # uid_attribute "ID_SERIAL" # prio "const" # prio_args "" # features "0" # path_checker "directio" # alias_prefix "mpath" # failback "manual" # rr_min_io 1000 # rr_min_io_rq 1 # max_fds "max" # rr_weight "uniform" # queue_without_daemon "yes" # flush_on_last_del "no" # user_friendly_names "no" # fast_io_fail_tmo 5 # bindings_file "/etc/multipath/bindings" # wwids_file /etc/multipath/wwids # log_checker_err always # retain_attached_hw_handler no # detect_prio no # config_dir "/etc/multipath/conf.d" # delay_watch_checks no # delay_wait_checks no #} #blacklist { # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^(td|hd|vd)[a-z]" # devnode "^dcssblk[0-9]*" # devnode "^nvme.*" # device { # vendor "DGC" # product "LUNZ" # } # device { # vendor "EMC" # product "LUNZ" # } # device { # vendor "IBM" # product "Universal Xport" # } # device { # vendor "IBM" # product "S/390.*" # } # device { # vendor "DELL" # product "Universal Xport" # } # device { # vendor "SGI" # product "Universal Xport" # } # device { # vendor "STK" # product "Universal Xport" # } # device { # vendor "SUN" # product "Universal Xport" # } # device { # vendor "(LSI|ENGENIO)" # product "Universal Xport" # } #} #blacklist_exceptions { # property "(SCSI_IDENT_.*|ID_WWN)" #} #devices { # device { # vendor "COMPELNT" # product "Compellent Vol" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "APPLE*" # product "Xserve RAID " # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "3PARdata" # product "VV" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "DEC" # product "HSG80" # path_grouping_policy "group_by_prio" # path_checker "hp_sw" # features "1 queue_if_no_path" # hardware_handler "1 hp_sw" # prio "hp_sw" # rr_weight "uniform" # } # device { # vendor "HP" # product "A6189A" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # no_path_retry 12 # } # device { # vendor "(COMPAQ|HP)" # product "(MSA|HSV)1.0.*" # path_grouping_policy "group_by_prio" # path_checker "hp_sw" # features "1 queue_if_no_path" # hardware_handler "1 hp_sw" # prio "hp_sw" # rr_weight "uniform" # no_path_retry 12 # rr_min_io 100 # } # device { # vendor "(COMPAQ|HP)" # product "MSA VOLUME" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 12 # rr_min_io 100 # } # device { # vendor "(COMPAQ|HP)" # product "HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 12 # rr_min_io 100 # } # device { # vendor "HP" # product "MSA2[02]12fc|MSA2012i" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # failback "immediate" # rr_weight "uniform" # no_path_retry 18 # rr_min_io 100 # } # device { # vendor "HP" # product "MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 18 # rr_min_io 100 # } # device { # vendor "HP" # product "HSVX700" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 alua" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 12 # rr_min_io 100 # } # device { # vendor "HP" # product "LOGICAL VOLUME.*" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # no_path_retry 12 # } # device { # vendor "HP" # product "P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 18 # rr_min_io 100 # } # device { # vendor "DDN" # product "SAN DataDirector" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "EMC" # product "SYMMETRIX" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # no_path_retry 6 # } # device { # vendor "DGC" # product ".*" # product_blacklist "LUNZ" # path_grouping_policy "group_by_prio" # path_checker "emc_clariion" # features "1 queue_if_no_path" # hardware_handler "1 emc" # prio "emc" # failback "immediate" # rr_weight "uniform" # no_path_retry 60 # retain_attached_hw_handler yes # detect_prio yes # } # device { # vendor "EMC" # product "Invista" # product_blacklist "LUNZ" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # no_path_retry 5 # } # device { # vendor "FSC" # product "CentricStor" # path_grouping_policy "group_by_serial" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "FUJITSU" # product "ETERNUS_DX(L|M|400|8000)" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 10 # } # device { # vendor "(HITACHI|HP)" # product "OPEN-.*" # path_grouping_policy "multibus" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "HITACHI" # product "DF.*" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "hds" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "IBM" # product "ProFibre 4000R" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^1722-600" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "1 queue_if_no_path" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 300 # } # device { # vendor "IBM" # product "^1724" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "1 queue_if_no_path" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 300 # } # device { # vendor "IBM" # product "^1726" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "1 queue_if_no_path" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 300 # } # device { # vendor "IBM" # product "^1742" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "IBM" # product "^1745|^1746" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "IBM" # product "^1814" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "IBM" # product "^1815" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "IBM" # product "^1818" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "IBM" # product "^3526" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "IBM" # product "^3542" # path_grouping_policy "group_by_serial" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^2105800" # path_grouping_policy "group_by_serial" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^2105F20" # path_grouping_policy "group_by_serial" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^1750500" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^2107900" # path_grouping_policy "multibus" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^2145" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "IBM" # product "S/390 DASD ECKD" # product_blacklist "S/390.*" # path_grouping_policy "multibus" # uid_attribute "ID_UID" # path_checker "directio" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "S/390 DASD FBA" # product_blacklist "S/390.*" # path_grouping_policy "multibus" # uid_attribute "ID_UID" # path_checker "directio" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "IBM" # product "^IPR.*" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "1 alua" # prio "alua" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "IBM" # product "1820N00" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # rr_min_io 100 # } # device { # vendor "IBM" # product "2810XIV" # path_grouping_policy "multibus" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # failback 15 # rr_weight "uniform" # rr_min_io 15 # } # device { # vendor "AIX" # product "VDASD" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # failback "immediate" # rr_weight "uniform" # no_path_retry 60 # } # device { # vendor "IBM" # product "3303 NVDISK" # path_grouping_policy "failover" # path_checker "tur" # features "0" # hardware_handler "0" # prio "const" # failback "immediate" # rr_weight "uniform" # no_path_retry 60 # } # device { # vendor "AIX" # product "NVDISK" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 alua" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry 60 # } # device { # vendor "DELL" # product "MD3000" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "DELL" # product "MD3000i" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "DELL" # product "MD32xx" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "DELL" # product "MD32xxi" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "DELL" # product "MD36xxi" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "DELL" # product "MD36xxf" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "NETAPP" # product "LUN.*" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "3 queue_if_no_path pg_init_retries 50" # hardware_handler "0" # prio "ontap" # failback "immediate" # rr_weight "uniform" # rr_min_io 128 # flush_on_last_del "yes" # dev_loss_tmo "infinity" # retain_attached_hw_handler "yes" # detect_prio "yes" # } # device { # vendor "NEXENTA" # product "COMSTAR" # path_grouping_policy "group_by_serial" # path_checker "directio" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # failback "immediate" # rr_weight "uniform" # no_path_retry 30 # rr_min_io 128 # } # device { # vendor "IBM" # product "Nseries.*" # path_grouping_policy "group_by_prio" # path_checker "directio" # features "1 queue_if_no_path" # hardware_handler "0" # prio "ontap" # failback "immediate" # rr_weight "uniform" # rr_min_io 128 # } # device { # vendor "Pillar" # product "Axiom.*" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # rr_weight "uniform" # } # device { # vendor "SGI" # product "TP9[13]00" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "SGI" # product "TP9[45]00" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "SGI" # product "IS.*" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 15 # } # device { # vendor "NEC" # product "DISK ARRAY" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 alua" # prio "alua" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "STK" # product "OPENstorage D280" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "SUN" # product "(StorEdge 3510|T4)" # path_grouping_policy "multibus" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "SUN" # product "STK6580_6780" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # } # device { # vendor "EUROLOGC" # product "FC2502" # path_grouping_policy "group_by_prio" # path_checker "directio" # features "0" # hardware_handler "0" # prio "const" # rr_weight "uniform" # } # device { # vendor "PIVOT3" # product "RAIGE VOLUME" # path_grouping_policy "multibus" # path_checker "tur" # features "1 queue_if_no_path" # hardware_handler "0" # prio "const" # rr_weight "uniform" # rr_min_io 100 # } # device { # vendor "SUN" # product "CSM200_R" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "SUN" # product "LCSM100_[IEFS]" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "SUN" # product "SUN_6180" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # rr_min_io 1000 # rr_min_io_rq 1 # } # device { # vendor "(NETAPP|LSI|ENGENIO)" # product "INF-01-00" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry 30 # detect_prio "yes" # retain_attached_hw_handler "yes" # } # device { # vendor "STK" # product "FLEXLINE 380" # product_blacklist "Universal Xport" # path_grouping_policy "group_by_prio" # path_checker "rdac" # features "0" # hardware_handler "1 rdac" # prio "rdac" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "Intel" # product "Multi-Flex" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "1 alua" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "DataCore" # product "SANmelody" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } # device { # vendor "DataCore" # product "Virtual Disk" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "0" # hardware_handler "0" # prio "alua" # failback "immediate" # rr_weight "uniform" # no_path_retry "queue" # } #} #multipaths { #} #overrides { #} multipath-tools-0.5.0+git1.656f8865/multipath.conf.synthetic000066400000000000000000000032331260771327300234410ustar00rootroot00000000000000## ## This is a template multipath-tools configuration file ## Uncomment the lines relevent to your environment ## #defaults { # udev_dir /dev # polling_interval 10 # path_selector "round-robin 0" # path_grouping_policy multibus # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # prio const # path_checker directio # rr_min_io 100 # flush_on_last_del no # max_fds 8192 # rr_weight priorities # failback immediate # no_path_retry fail # queue_without_daemon no # user_friendly_names no # mode 644 # uid 0 # gid disk #} #blacklist { # wwid 26353900f02796769 # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^hd[a-z][[0-9]*]" # device { # vendor DEC.* # product MSA[15]00 # } #} #blacklist_exceptions { # devnode "^dasd[c-d]+[0-9]*" # wwid "IBM.75000000092461.4d00.34" #} #multipaths { # multipath { # wwid 3600508b4000156d700012000000b0000 # alias yellow # path_grouping_policy multibus # path_selector "round-robin 0" # failback manual # rr_weight priorities # no_path_retry 5 # rr_min_io 100 # } # multipath { # wwid 1DEC_____321816758474 # alias red # } #} #devices { # device { # vendor "COMPAQ " # product "HSV110 (C)COMPAQ" # path_grouping_policy multibus # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # path_checker directio # path_selector "round-robin 0" # hardware_handler "0" # failback 15 # rr_weight priorities # no_path_retry queue # rr_min_io 100 # product_blacklist LUNZ # } # device { # vendor "COMPAQ " # product "MSA1000 " # path_grouping_policy multibus # } #} #overrides { # no_path_retry fail #} multipath-tools-0.5.0+git1.656f8865/multipath/000077500000000000000000000000001260771327300205605ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/multipath/01_udev000077500000000000000000000015451260771327300217560ustar00rootroot00000000000000#!/bin/sh # cp /sbin/udev $INITRDDIR/sbin/hotplug cp /sbin/udevstart $INITRDDIR/sbin/ cp /bin/mountpoint $INITRDDIR/bin/ cp /bin/readlink $INITRDDIR/bin/ PROGS="/sbin/udev /sbin/udevstart /bin/mountpoint /bin/readlink" LIBS=`ldd $PROGS | grep -v linux-gate.so | sort -u | \ awk '{print $3}'` for i in $LIBS do mkdir -p `dirname $INITRDDIR/$i` cp $i $INITRDDIR/$i done # # config files # if [ -d /etc/dev.d ] then cp -a /etc/dev.d $INITRDDIR/etc/ fi if [ -d /etc/udev ] then cp -a /etc/udev $INITRDDIR/etc/ fi # # run udev from initrd # cat <| $INITRDDIR/scripts/10_udev.sh cd / mount -nt proc proc proc mount -nt sysfs sysfs sys mount -nt tmpfs tmpfs dev || mount -nt ramfs ramfs dev mount -nt tmpfs tmpfs tmp || mount -nt ramfs ramfs tmp #modprobe dm-mod #modprobe dm-multipath /sbin/udevstart umount -n tmp umount -n sys umount -n proc sleep 2 EOF multipath-tools-0.5.0+git1.656f8865/multipath/02_multipath000077500000000000000000000013071260771327300230170ustar00rootroot00000000000000#!/bin/sh # # store the multipath tool in the initrd # hotplug & udev will take care of calling it when appropriate # this tool is statically linked against klibc : no additional libs # cp /sbin/multipath $INITRDDIR/sbin cp /sbin/kpartx $INITRDDIR/sbin # # feed the dependencies too # scsi_id is dynamicaly linked, so store the libs too # cp /lib/udev/scsi_id $INITRDDIR/lib/udev/ cp /bin/mountpoint $INITRDDIR/bin PROGS="/lib/udev/scsi_id /bin/mountpoint" LIBS=`ldd $PROGS | grep -v linux-gate.so | sort -u | \ awk '{print $3}'` for i in $LIBS do mkdir -p `dirname $INITRDDIR/$i` cp $i $INITRDDIR/$i done # # config file ? # if [ -f /etc/multipath.conf ] then cp /etc/multipath.conf $INITRDDIR/etc/ fi multipath-tools-0.5.0+git1.656f8865/multipath/11-dm-mpath.rules000066400000000000000000000033051260771327300235630ustar00rootroot00000000000000ACTION!="add|change", GOTO="mpath_end" ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end" ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end" # Do not initiate scanning if no path is available, # otherwise there would be a hang or IO error on access. # We'd like to avoid this, especially within udev processing. ENV{DM_NR_VALID_PATHS}!="?*", IMPORT{db}="DM_NR_VALID_PATHS" ENV{DM_NR_VALID_PATHS}!="0", GOTO="mpath_blkid_end" IMPORT{db}="ID_FS_TYPE" IMPORT{db}="ID_FS_USAGE" IMPORT{db}="ID_FS_UUID" IMPORT{db}="ID_FS_UUID_ENC" IMPORT{db}="ID_FS_VERSION" LABEL="mpath_blkid_end" # Also skip all foreign rules if no path is available. # Remember the original value of DM_DISABLE_OTHER_RULES_FLAG # and restore it back once we have at least one path available. IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" ENV{DM_ACTION}=="PATH_FAILED",\ ENV{DM_NR_VALID_PATHS}=="0",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" ENV{DM_ACTION}=="PATH_REINSTATED",\ ENV{DM_NR_VALID_PATHS}=="1",\ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\ ENV{DM_ACTIVATION}="1" # DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem. # Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its # paths are lost/recovered. For any stack above the mpath device, this is not # something that should be reacted upon since it would be useless extra work. # It's exactly mpath's job to provide *seamless* device access to any of the # paths that are available underneath. ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0" LABEL="mpath_end" multipath-tools-0.5.0+git1.656f8865/multipath/Makefile000066400000000000000000000022651260771327300222250ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc OBJS = main.o CFLAGS += -I$(multipathdir) LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev EXEC = multipath all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ $(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir) $(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) rm $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules rm $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules rm $(DESTDIR)$(mandir)/$(EXEC).8.gz rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.5.0+git1.656f8865/multipath/main.c000066400000000000000000000406401260771327300216540ustar00rootroot00000000000000/* * Soft: multipath device mapper target autoconfig * * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ * * Author: Christophe Varoqui * * 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. * * 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. * * Copyright (c) 2003, 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int logsink; static int filter_pathvec (vector pathvec, char * refwwid) { int i; struct path * pp; if (!refwwid || !strlen(refwwid)) return 0; vector_foreach_slot (pathvec, pp, i) { if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) { condlog(3, "skip path %s : out of scope", pp->dev); free_path(pp); vector_del_slot(pathvec, i); i--; } } return 0; } static void usage (char * progname) { fprintf (stderr, VERSION_STRING); fprintf (stderr, "Usage:\n"); fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname); fprintf (stderr, " %s -F [-v lvl]\n", progname); fprintf (stderr, " %s -t\n", progname); fprintf (stderr, " %s -h\n", progname); fprintf (stderr, "\n" "Where:\n" " -h print this usage text\n" \ " -l show multipath topology (sysfs and DM info)\n" \ " -ll show multipath topology (maximum info)\n" \ " -f flush a multipath device map\n" \ " -F flush all multipath device maps\n" \ " -a add a device wwid to the wwids file\n" \ " -c check if a device should be a path in a multipath device\n" \ " -q allow queue_if_no_path when multipathd is not running\n"\ " -d dry run, do not create or update devmaps\n" \ " -t dump internal hardware table\n" \ " -r force devmap reload\n" \ " -i ignore wwids file\n" \ " -B treat the bindings file as read only\n" \ " -p policy failover|multibus|group_by_serial|group_by_prio\n" \ " -b fil bindings file location\n" \ " -w remove a device from the wwids file\n" \ " -W reset the wwids file include only the current devices\n" \ " -p pol force all maps to specified path grouping policy :\n" \ " . failover one path per priority group\n" \ " . multibus all paths in one priority group\n" \ " . group_by_serial one priority group per serial\n" \ " . group_by_prio one priority group per priority lvl\n" \ " . group_by_node_name one priority group per target node\n" \ " -v lvl verbosity level\n" \ " . 0 no output\n" \ " . 1 print created devmap names only\n" \ " . 2 default verbosity\n" \ " . 3 print debug information\n" \ " dev action limited to:\n" \ " . multipath named 'dev' (ex: mpath0) or\n" \ " . multipath whose wwid is 'dev' (ex: 60051..)\n" \ " . multipath including the path named 'dev' (ex: /dev/sda)\n" \ " . multipath including the path with maj:min 'dev' (ex: 8:0)\n" \ ); } static int update_paths (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) { if (!strlen(pp->dev)) { if (devt2devname(pp->dev, FILE_NAME_SIZE, pp->dev_t)) { /* * path is not in sysfs anymore */ pp->chkrstate = pp->state = PATH_DOWN; continue; } pp->mpp = mpp; if (pathinfo(pp, conf->hwtable, DI_ALL)) pp->state = PATH_UNCHECKED; continue; } pp->mpp = mpp; if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) { if (pathinfo(pp, conf->hwtable, DI_CHECKER)) pp->state = PATH_UNCHECKED; } if (pp->priority == PRIO_UNDEF) { if (pathinfo(pp, conf->hwtable, DI_PRIO)) pp->priority = PRIO_UNDEF; } } } return 0; } static int get_dm_mpvec (vector curmp, vector pathvec, char * refwwid) { int i; struct multipath * mpp; char params[PARAMS_SIZE], status[PARAMS_SIZE]; if (dm_get_maps(curmp)) return 1; vector_foreach_slot (curmp, mpp, i) { /* * discard out of scope maps */ if (mpp->wwid && refwwid && strncmp(mpp->wwid, refwwid, WWID_SIZE)) { condlog(3, "skip map %s: out of scope", mpp->alias); free_multipath(mpp, KEEP_PATHS); vector_del_slot(curmp, i); i--; continue; } if (conf->cmd == CMD_VALID_PATH) continue; dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); condlog(3, "status = %s", status); disassemble_map(pathvec, params, mpp); /* * disassemble_map() can add new paths to pathvec. * If not in "fast list mode", we need to fetch information * about them */ if (conf->cmd != CMD_LIST_SHORT) update_paths(mpp); if (conf->cmd == CMD_LIST_LONG) mpp->bestpg = select_path_group(mpp); disassemble_status(status, mpp); if (conf->cmd == CMD_LIST_SHORT || conf->cmd == CMD_LIST_LONG) print_multipath_topology(mpp, conf->verbosity); if (conf->cmd == CMD_CREATE) reinstate_paths(mpp); } return 0; } /* * Return value: * -1: Retry * 0: Success * 1: Failure */ static int configure (void) { vector curmp = NULL; vector pathvec = NULL; struct vectors vecs; int r = 1; int di_flag = 0; char * refwwid = NULL; char * dev = NULL; /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc(); pathvec = vector_alloc(); if (!curmp || !pathvec) { condlog(0, "can not allocate memory"); goto out; } vecs.pathvec = pathvec; vecs.mpvec = curmp; dev = convert_dev(conf->dev, (conf->dev_type == DEV_DEVNODE)); /* * if we have a blacklisted device parameter, exit early */ if (dev && conf->dev_type == DEV_DEVNODE && conf->cmd != CMD_REMOVE_WWID && (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) { if (conf->cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", conf->dev); goto out; } /* * scope limiting must be translated into a wwid * failing the translation is fatal (by policy) */ if (conf->dev) { int failed = get_refwwid(conf->dev, conf->dev_type, pathvec, &refwwid); if (!refwwid) { condlog(3, "%s: failed to get wwid", conf->dev); if (failed == 2 && conf->cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", conf->dev); else condlog(3, "scope is nul"); goto out; } if (conf->cmd == CMD_REMOVE_WWID) { r = remove_wwid(refwwid); if (r == 0) printf("wwid '%s' removed\n", refwwid); else if (r == 1) { printf("wwid '%s' not in wwids file\n", refwwid); r = 0; } goto out; } if (conf->cmd == CMD_ADD_WWID) { r = remember_wwid(refwwid); if (r == 0) printf("wwid '%s' added\n", refwwid); else printf("failed adding '%s' to wwids file\n", refwwid); goto out; } condlog(3, "scope limited to %s", refwwid); /* If you are ignoring the wwids file and find_multipaths is * 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 (conf->cmd == CMD_VALID_PATH && (!conf->find_multipaths || !conf->ignore_wwids)) { if (conf->ignore_wwids || check_wwids_file(refwwid, 0) == 0) r = 0; printf("%s %s a valid multipath device path\n", conf->dev, r == 0 ? "is" : "is not"); goto out; } } /* * get a path list */ if (conf->dev) di_flag = DI_WWID; if (conf->cmd == CMD_LIST_LONG) /* extended path info '-ll' */ di_flag |= DI_SYSFS | DI_CHECKER; else if (conf->cmd == CMD_LIST_SHORT) /* minimum path info '-l' */ di_flag |= DI_SYSFS; else /* maximum info */ di_flag = DI_ALL; if (path_discovery(pathvec, conf, di_flag) < 0) goto out; if (conf->verbosity > 2) print_all_paths(pathvec, 1); get_path_layout(pathvec, 0); if (get_dm_mpvec(curmp, pathvec, refwwid)) goto out; filter_pathvec(pathvec, refwwid); if (conf->cmd == CMD_VALID_PATH) { /* This only happens if find_multipaths is and * ignore_wwids is set. * 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) r = 0; printf("%s %s a valid multipath device path\n", conf->dev, r == 0 ? "is" : "is not"); goto out; } if (conf->cmd != CMD_CREATE && conf->cmd != CMD_DRY_RUN) { r = 0; goto out; } /* * core logic entry point */ r = coalesce_paths(&vecs, NULL, refwwid, conf->force_reload); out: if (refwwid) FREE(refwwid); free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); return r; } static int dump_config (void) { char * c, * tmp = NULL; char * reply; unsigned int maxlen = 256; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) { if (tmp) free(tmp); return 1; } c = tmp = reply; c += snprint_defaults(c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_blacklist(c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_blacklist_except(c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_overrides(c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } if (VECTOR_SIZE(conf->mptable) > 0) { c += snprint_mptable(c, reply + maxlen - c, conf->mptable); again = ((c - reply) == maxlen); if (again) reply = REALLOC(reply, maxlen *= 2); } } printf("%s", reply); FREE(reply); return 0; } static int get_dev_type(char *dev) { struct stat buf; int i; if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) { if (dm_is_dm_major(major(buf.st_rdev))) return DEV_DEVMAP; return DEV_DEVNODE; } else if (sscanf(dev, "%d:%d", &i, &i) == 2) return DEV_DEVT; else return DEV_DEVMAP; } int main (int argc, char *argv[]) { struct udev *udev; int arg; extern char *optarg; extern int optind; int r = 1; udev = udev_new(); logsink = 0; if (load_config(DEFAULT_CONFIGFILE, udev)) exit(1); while ((arg = getopt(argc, argv, ":adchl::FfM:v:p:b:BritquwW")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; case 'v': if (sizeof(optarg) > sizeof(char *) || !isdigit(optarg[0])) { usage (argv[0]); exit(1); } conf->verbosity = atoi(optarg); break; case 'b': conf->bindings_file = strdup(optarg); break; case 'B': conf->bindings_read_only = 1; break; case 'q': conf->allow_queueing = 1; break; case 'c': conf->cmd = CMD_VALID_PATH; break; case 'd': if (conf->cmd == CMD_CREATE) conf->cmd = CMD_DRY_RUN; break; case 'f': conf->remove = FLUSH_ONE; break; case 'F': conf->remove = FLUSH_ALL; break; case 'l': if (optarg && !strncmp(optarg, "l", 1)) conf->cmd = CMD_LIST_LONG; else conf->cmd = CMD_LIST_SHORT; break; case 'M': #if _DEBUG_ debug = atoi(optarg); #endif break; case 'p': conf->pgpolicy_flag = get_pgpolicy_id(optarg); if (conf->pgpolicy_flag == -1) { printf("'%s' is not a valid policy\n", optarg); usage(argv[0]); exit(1); } break; case 'r': conf->force_reload = 1; break; case 'i': conf->ignore_wwids = 1; break; case 't': r = dump_config(); goto out_free_config; case 'h': usage(argv[0]); exit(0); case 'u': conf->cmd = CMD_VALID_PATH; conf->dev_type = DEV_UEVENT; break; case 'w': conf->cmd = CMD_REMOVE_WWID; break; case 'W': conf->cmd = CMD_RESET_WWIDS; break; case 'a': conf->cmd = CMD_ADD_WWID; break; case ':': fprintf(stderr, "Missing option argument\n"); usage(argv[0]); exit(1); case '?': fprintf(stderr, "Unknown switch: %s\n", optarg); usage(argv[0]); exit(1); default: usage(argv[0]); exit(1); } } if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } if (dm_prereq()) exit(1); dm_drv_version(conf->version, TGT_MPATH); dm_udev_set_sync_support(1); if (optind < argc) { conf->dev = MALLOC(FILE_NAME_SIZE); if (!conf->dev) goto out; strncpy(conf->dev, argv[optind], FILE_NAME_SIZE); if (conf->dev_type != DEV_UEVENT) conf->dev_type = get_dev_type(conf->dev); } conf->daemon = 0; if (conf->dev_type == DEV_UEVENT) { openlog("multipath", 0, LOG_DAEMON); setlogmask(LOG_UPTO(conf->verbosity + 3)); logsink = 1; } if (conf->max_fds) { struct rlimit fd_limit; fd_limit.rlim_cur = conf->max_fds; fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) condlog(0, "can't set open fds limit to %d : %s", conf->max_fds, strerror(errno)); } if (init_checkers()) { condlog(0, "failed to initialize checkers"); goto out; } if (init_prio()) { condlog(0, "failed to initialize prioritizers"); goto out; } dm_init(); if (conf->cmd == CMD_VALID_PATH && (!conf->dev || conf->dev_type == DEV_DEVMAP)) { condlog(0, "the -c option requires a path to check"); goto out; } if (conf->cmd == CMD_VALID_PATH && conf->dev_type == DEV_UEVENT) { int fd; fd = ux_socket_connect(DEFAULT_SOCKET); if (fd == -1) { printf("%s is not a valid multipath device path\n", conf->dev); goto out; } close(fd); } if (conf->cmd == CMD_REMOVE_WWID && !conf->dev) { condlog(0, "the -w option requires a device"); goto out; } if (conf->cmd == CMD_RESET_WWIDS) { struct multipath * mpp; int i; vector curmp; curmp = vector_alloc(); if (!curmp) { condlog(0, "can't allocate memory for mp list"); goto out; } if (dm_get_maps(curmp) == 0) r = replace_wwids(curmp); if (r == 0) printf("successfully reset wwids\n"); vector_foreach_slot_backwards(curmp, mpp, i) { vector_del_slot(curmp, i); free_multipath(mpp, KEEP_PATHS); } vector_free(curmp); goto out; } if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) { r = dm_suspend_and_flush_map(conf->dev); } else condlog(0, "must provide a map name to remove"); goto out; } else if (conf->remove == FLUSH_ALL) { r = dm_flush_maps(); goto out; } while ((r = configure()) < 0) condlog(3, "restart multipath configuration process"); out: dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); if (conf->dev_type == DEV_UEVENT) closelog(); out_free_config: /* * Freeing config must be done after dm_lib_exit(), because * the logging function (dm_write_log()), which is called there, * references the config. */ free_config(conf); conf = NULL; udev_unref(udev); #ifdef _DEBUG_ dbg_free_final(NULL); #endif return r; } multipath-tools-0.5.0+git1.656f8865/multipath/multipath.8000066400000000000000000000061621260771327300226650ustar00rootroot00000000000000.TH MULTIPATH 8 "July 2006" "" "Linux Administrator's Manual" .SH NAME multipath \- Device mapper target autoconfig .SH SYNOPSIS .B multipath .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-b\ \c .IR bindings_file \|] .RB [\| \-d \|] .RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \|-i | \-a | \|-u | \-w | \-W \|] .RB [\| \-p\ \c .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| device \|] .SH DESCRIPTION .B multipath is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons. .SH OPTIONS .TP .B \-v " level" verbosity, print all paths and multipaths .RS 1.2i .TP 1.2i .B 0 no output .TP .B 1 print the created or updated multipath names only, for use to feed other tools like kpartx .TP .B 2 + print all info : detected paths, coalesced paths (ie multipaths) and device maps .RE .TP .B \-h print usage text .TP .B \-d dry run, do not create or update devmaps .TP .B \-l show the current multipath topology from information fetched in sysfs and the device mapper .TP .B \-ll show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...) .TP .B \-f flush a multipath device map specified as parameter, if unused .TP .B \-F flush all unused multipath device maps .TP .B \-t print internal hardware table to stdout .TP .B \-r force devmap reload .TP .B \-i ignore wwids file when processing devices .TP .B \-B treat the bindings file as read only .TP .B \-b " bindings_file" set user_friendly_names bindings file location. The default is /etc/multipath/bindings .TP .B \-c check if a block device should be a path in a multipath device .TP .B \-q allow device tables with queue_if_no_path when multipathd is not running .TP .B \-a add the wwid for the specified device to the wwids file .TP .B \-u check if the device specified in the program environment should be a path in a multipath device. .TP .B \-w remove the wwid for the specified device from the wwids file .TP .B \-W reset the wwids file to only include the current multipath devices .TP .BI \-p " policy" force new maps to use the specified policy: .RS 1.2i .TP 1.2i .B failover 1 path per priority group .TP .B multibus all paths in 1 priority group .TP .B group_by_serial 1 priority group per serial .TP .B group_by_prio 1 priority group per priority value. Priorities are determined by callout programs specified as a global, per-controller or per-multipath option in the configuration file .TP .B group_by_node_name 1 priority group per target node name. Target node names are fetched in /sys/class/fc_transport/target*/node_name. .TP .RE Existing maps are not modified. .TP .BI device update only the devmap specified by .IR device , which is either: .RS 1.2i .IP \[bu] a devmap name .IP \[bu] a path associated with the desired devmap; the path may be in one of the following formats: .RS 1.2i .IP \[bu] .B /dev/sdb .IP \[bu] .B major:minor .SH "SEE ALSO" .BR multipathd (8), .BR multipath.conf (5), .BR kpartx (8), .BR udev (8), .BR dmsetup (8) .BR hotplug (8) .SH AUTHORS .B multipath was developed by Christophe Varoqui, and others. multipath-tools-0.5.0+git1.656f8865/multipath/multipath.conf.5000066400000000000000000000516471260771327300236160ustar00rootroot00000000000000.TH MULTIPATH.CONF 5 "30 November 2006" .SH NAME multipath.conf \- multipath daemon configuration file .SH DESCRIPTION .B "multipath.conf" is the configuration file for the multipath daemon. It is used to overwrite the built-in configuration table of \fBmultipathd\fP. Any line whose first non-white-space character is a '#' is considered a comment line. Empty lines are ignored. .SH SYNTAX The configuration file contains entries of the form: .RS .nf .ft B .sp
{ .RS .ft B .I "..." .ft B { .RS .ft B .I "..." .RE } .RE } .ft R .fi .RE .LP Each \fIsection\fP contains one or more attributes or subsections. The recognized keywords for attributes or subsections depend on the section in which they occur. .LP The following \fIsection\fP keywords are recognized: .TP 17 .B defaults This section defines default values for attributes which are used whenever no values are given in the appropriate device or multipath sections. .TP .B blacklist This section defines which devices should be excluded from the multipath topology discovery. .TP .B blacklist_exceptions This section defines which devices should be included in the multipath topology discovery, despite being listed in the .I blacklist section. .TP .B multipaths This section defines the multipath topologies. They are indexed by a \fIWorld Wide Identifier\fR(wwid). For details on the wwid generation see section \fBWWID generation\fR below. .TP .B devices This section defines the device-specific settings. .TP .B overrides This section defines values for attributes that should override the device-specific settings for all devices. .RE .LP .SH "defaults section" The .B defaults section recognizes the following keywords: .TP 17 .B polling_interval interval between two path checks in seconds. For properly functioning paths, the interval between checks will gradually increase to .B max_polling_interval. This value will be overridden by the .B WatchdogSec setting in the multipathd.service definition if systemd is used. Default is .I 5 .TP .B max_polling_interval maximal interval between two path checks in seconds; default is .I 4 * polling_interval .TP .B multipath_dir directory where the dynamic shared objects are stored; default is system dependent, commonly .I /lib/multipath .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 remeber 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; Default is .I no .TP .B verbosity default verbosity. Higher values increase the verbosity level. Valid levels are between 0 and 6; default is .I 2 .TP .B reassign_maps enable reassigning of device-mapper maps. With this option multipathd will remap existing device-mapper maps to always point to multipath device, not the underlying block devices. Possible values are \fIyes\fR and \fIno\fR. Default is .I yes .TP .B path_selector The default path selector algorithm to use; they are offered by the kernel multipath target. There are three selector algorithms. .RS .TP 12 .B "round-robin 0" Loop through every path in the path group, sending the same amount of IO to each. .TP .B "queue-length 0" Send the next bunch of IO down the path with the least amount of outstanding IO. .TP .B "service-time 0" Choose the path for the next bunch of IO based on the amount of outstanding IO to the path and its relative throughput. .RE .TP .B path_grouping_policy The default path grouping policy to apply to unspecified multipaths. Possible values are .RS .TP 12 .B failover 1 path per priority group .TP .B multibus all paths in 1 priority group .TP .B group_by_serial 1 priority group per serial number .TP .B group_by_prio 1 priority group per priority value. Priorities are determined by callout programs specified as a global, per-controller or per-multipath option in the configuration file. .TP .B group_by_node_name 1 priority group per target node name. Target node names are fetched in /sys/class/fc_transport/target*/node_name. .TP Default value is \fIfailover\fR. .RE .TP .B uid_attribute The udev attribute providing a unique path identifier. Default value is .I ID_SERIAL .TP .B getuid_callout The default program and args to callout to obtain a unique path identifier. Should be specified with an absolute path. This parameter is deprecated. .TP .B prio The name of the path priority routine. The specified routine should return a numeric value specifying the relative priority of this path. Higher number have a higher priority. .I "none" is a valid value. Currently the following path priority routines are implemented: .RS .TP 12 .B const Return a constant priority of \fI1\fR. .TP .B emc Generate the path priority for EMC arrays. .TP .B alua Generate the path priority based on the SCSI-3 ALUA settings. .TP .B ontap Generate the path priority for NetApp arrays. .TP .B rdac Generate the path priority for LSI/Engenio/NetApp E-Series RDAC controller. .TP .B hp_sw Generate the path priority for Compaq/HP controller in active/standby mode. .TP .B hds Generate the path priority for Hitachi HDS Modular storage arrays. .TP .B random Generate a random priority between 1 and 10. .TP 12 .B weightedpath Generate the path priority based on the regular expression and the priority provided as argument. requires prio_args keyword. .TP Default value is \fBnone\fR. .RE .TP .B prio_args Arguments to pass to to the prio function. Currently only used with .I weighted, which needs a value of the form .I " ..." .I hbtl regex can be of SCSI H:B:T:L format Ex: 1:0:.:. , *:0:0:. .I devname regex can be of device name format Ex: sda , sd.e .TP .B features Specify any device-mapper features to be used. Syntax is .I num list where .I num is the number of features in .I list. Possible values for the feature list are .RS .TP 12 .B queue_if_no_path Queue IO if no path is active; identical to the .I no_path_retry keyword. .TP .B no_partitions Disable automatic partitions generation via kpartx. .RE .TP .B path_checker The default method used to determine the paths state. Possible values are .RS .TP 12 .B readsector0 (Deprecated) Read the first sector of the device. This checker is being deprecated, please use \fIdirectio\fR instead .TP .B tur Issue a .I TEST UNIT READY command to the device. .TP .B emc_clariion Query the EMC Clariion specific EVPD page 0xC0 to determine the path state. .TP .B hp_sw Check the path state for HP storage arrays with Active/Standby firmware. .TP .B rdac Check the path state for LSI/Engenio/NetApp E-Series RDAC storage controller. .TP .B directio Read the first sector with direct I/O. .TP Default value is \fIdirectio\fR. .RE .TP .B failback Tell multipathd how to manage path group failback. .RS .TP 12 .B immediate Immediately failback to the highest priority pathgroup that contains active paths. .TP .B manual Do not perform automatic failback. .TP .B followover Only perform automatic failback when the first path of a pathgroup becomes active. This keeps a node from automatically failing back when another node requested the failover. .TP .B values > 0 deferred failback (time to defer in seconds) .TP Default value is \fImanual\fR. .RE .TP .B rr_min_io The number of IO to route to a path before switching to the next in the same path group. This is only for BIO based multipath. Default is .I 1000 .TP .B rr_min_io_rq The number of IO requests to route to a path before switching to the next in the same path group. This is only for request based multipath. Default is .I 1 .TP .B rr_weight If set to \fIpriorities\fR the multipath configurator will assign path weights as "path prio * rr_min_io". Possible values are .I priorities or .IR uniform . Default is .IR uniform . .TP .B no_path_retry Specify the number of retries until disable queueing, or .I fail for immediate failure (no queueing), .I queue for never stop queueing. If unset no queueing is attempted. Default is unset. .TP .B user_friendly_names If set to .I yes , using the bindings file .I /etc/multipath/bindings to assign a persistent and unique alias to the multipath, in the form of mpath. If set to .I no use the WWID as the alias. In either case this be will be overridden by any specific aliases in the \fImultipaths\fR section. Default is .I no .TP .B flush_on_last_del If set to .I yes , multipathd will disable queueing when the last path to a device has been deleted. Default is .I no .TP .B max_fds Specify the maximum number of file descriptors that can be opened by multipath and multipathd. This is equivalent to ulimit \-n. A value of \fImax\fR will set this to the system limit from /proc/sys/fs/nr_open. If this is not set, the maximum number of open fds is taken from the calling process. It is usually 1024. To be safe, this should be set to the maximum number of paths plus 32, if that number is greated than 1024. .TP .B checker_timeout Specify the timeout to use for path checkers and prioritizers that issue scsi commands with an explicit timeout, in seconds; default taken from .I /sys/block/sd/device/timeout .TP .B fast_io_fail_tmo Specify the number of seconds the scsi layer will wait after a problem has been detected on a FC remote port before failing IO to devices on that remote port. This should be smaller than dev_loss_tmo. Setting this to .I off will disable the timeout. .TP .B dev_loss_tmo Specify the number of seconds the scsi layer will wait after a problem has been detected on a FC remote port before removing it from the system. This can be set to "infinity" which sets it to the max value of 2147483647 seconds, or 68 years. It will be automatically adjusted to the overall retry interval \fIno_path_retry\fR * \fIpolling_interval\fR if a number of retries is given with \fIno_path_retry\fR and the overall retry interval is longer than the specified \fIdev_loss_tmo\fR value. The linux kernel will cap this value to \fI300\fR if \fBfast_io_fail_tmo\fR is not set. .TP .B queue_without_daemon If set to .I no , when multipathd stops, queueing will be turned off for all devices. This is useful for devices that set no_path_retry. If a machine is shut down while all paths to a device are down, it is possible to hang waiting for IO to return from the device after multipathd has been stopped. Without multipathd running, access to the paths cannot be restored, and the kernel cannot be told to stop queueing IO. Setting queue_without_daemon to .I no , avoids this problem. Default is .I yes .TP .B bindings_file The full pathname of the binding file to be used when the user_friendly_names option is set. Defaults to .I /etc/multipath/bindings .TP .B wwids_file The full pathname of the wwids file, which is used by multipath to keep track of the wwids for LUNs it has created multipath devices on in the past. Defaults to .I /etc/multipath/wwids .TP .B log_checker_err If set to .I once , multipathd logs the first path checker error at logging level 2. Any later errors are logged at level 3 until the device is restored. If set to .I always , multipathd always logs the path checker error at logging level 2. Default is .I always .TP .B reservation_key This is the service action reservation key used by mpathpersist. It must be set for all multipath devices using persistent reservations, and it must be the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter list which contains an 8-byte value provided by the application client to the device server to identify the I_T nexus. It is unset by default. .TP .B retain_attached_hw_handler If set to .I yes and the scsi layer has already attached a hardware_handler to the device, multipath will not force the device to use the hardware_handler specified by mutipath.conf. If the scsi layer has not attached a hardware handler, multipath will continue to use its configured hardware handler. Default is .I no .TP .B detect_prio If set to .I yes , multipath will try to detect if the device supports ALUA. If so, the device will automatically use the .I alua prioritizer. If not, the prioritizer will be selected as usual. Default is .I no .TP .B force_sync If set to .I yes , multipathd will call the path checkers in sync mode only. This means that only one checker will run at a time. This is useful in the case where many multipathd checkers running in parallel causes significant CPU pressure. The Default is .I no .TP .B deferred_remove If set to .I yes , multipathd will do a deferred remove instead of a regular remove when the last path device has been deleted. This means that if the multipath device is still in use, it will be freed when the last user closes it. If path is added to the multipath device before the last user closes it, the deferred remove will be canceled. Default is .I no .TP .B config_dir If set to anything other than "", multipath will search this directory alphabetically for file ending in ".conf" and it will read configuration information from them, just as if it was in /etc/multipath.conf. config_dir must either be "" or a fully qualified directory name. Default is .I "/etc/multipath/conf.d" .TP .B delay_watch_checks If set to a value greater than 0, multipathd will watch paths that have recently become valid for this many checks. If they fail again while they are being watched, when they next become valid, they will not be used until they have stayed up for .I delay_wait_checks checks. Default is .I no .TP .B delay_wait_checks If set to a value greater than 0, when a device that has recently come back online fails again within .I delay_watch_checks checks, the next time it comes back online, it will marked and delayed, and not used until it has passed .I delay_wait_checks checks. Default is .I no .TP .B uxsock_timeout CLI receive timeout in milliseconds. For larger systems CLI commands might timeout before the multipathd lock is released and the CLI command can be processed. This will result in errors like 'timeout receiving packet' to be returned from CLI commands. In these cases it is recommended to increase the CLI timeout to avoid those issues. The default is .I 1000 . .SH "blacklist section" The .I blacklist section is used to exclude specific device from inclusion in the multipath topology. It is most commonly used to exclude local disks or LUNs for the array controller. .LP The following keywords are recognized: .TP 17 .B wwid The \fIWorld Wide Identification\fR of a device. .TP .B devnode Regular expression of the device nodes to be excluded. .TP .B property Regular expression of the udev property to be excluded. .TP .B device Subsection for the device description. This subsection recognizes the .I vendor and .I product keywords. For a full description of these keywords please see the .I devices section description. .SH "blacklist_exceptions section" The .I blacklist_exceptions section is used to revert the actions of the .I blacklist section, ie to include specific device in the multipath topology. This allows one to selectively include devices which would normally be excluded via the .I blacklist section. .LP The following keywords are recognized: .TP 17 .B wwid The \fIWorld Wide Identification\fR of a device. .TP .B property Regular expression of the udev property to be whitelisted. Defaults to .I (ID_WWN|SCSI_IDENT_.*) .TP .B devnode Regular expression of the device nodes to be whitelisted. .TP .B device Subsection for the device description. This subsection recognizes the .I vendor and .I product keywords. For a full description of these keywords please see the .I devices section description. .LP The .I property blacklist and whitelist handling is different from the usual handling in the sense that the whitelist .B has to be set, otherwise the device will be blacklisted. In these cases the message .I blacklisted, udev property missing will be displayed. .SH "multipaths section" The only recognized attribute for the .B multipaths section is the .I multipath subsection. .LP The .B multipath subsection recognizes the following attributes: .TP 17 .B wwid Index of the container. Mandatory for this subsection. .TP .B alias (Optional) symbolic name for the multipath map. .LP The following attributes are optional; if not set the default values are taken from the .I defaults or .I devices section: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B path_selector .TP .B prio .TP .B prio_args .TP .B failback .TP .B rr_weight .TP .B flush_on_last_del .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B features .TP .B reservation_key .TP .B deferred_remove .TP .B delay_watch_checks .TP .B delay_wait_checks .RE .PD .LP .SH "devices section" The only recognized attribute for the .B devices section is the .I device subsection. .LP The .I device subsection recognizes the following attributes: .TP 17 .B vendor (Mandatory) Vendor identifier .TP .B product (Mandatory) Product identifier .TP .B revision (Optional) Revision identfier .TP .B product_blacklist (Optional) Product strings to blacklist for this vendor .TP .B alias_prefix (Optional) The user_friendly_names prefix to use for this device type, instead of the default "mpath" .TP .B hardware_handler (Optional) The hardware handler to use for this device type. The following hardware handler are implemented: .RS .TP 12 .B 1 emc Hardware handler for EMC storage arrays. .TP .B 1 rdac Hardware handler for LSI/Engenio/NetApp E-Series RDAC storage controller. .TP .B 1 hp_sw Hardware handler for Compaq/HP storage arrays in active/standby mode. .TP .B 1 alua Hardware handler for SCSI-3 ALUA compatible arrays. .RE .LP The following attributes are optional; if not set the default values are taken from the .I defaults section: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B uid_attribute .TP .B path_selector .TP .B path_checker .TP .B prio .TP .B prio_args .TP .B features .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B fast_io_fail_tmo .TP .B dev_loss_tmo .TP .B flush_on_last_del .TP .B retain_attached_hw_handler .TP .B detect_prio .TP .B deferred_remove .TP .B delay_watch_checks .TP .B delay_wait_checks .RE .PD .LP .SH "overrides section" The overrides section recognizes the following optional attributes; if not set the values are taken from the .I devices or .I defaults sections: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B uid_attribute .TP .B getuid_callout .TP .B path_selector .TP .B path_checker .TP .B alias_prefix .TP .B features .TP .B prio .TP .B prio_args .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B flush_on_last_del .TP .B fast_io_fail_tmo .TP .B dev_loss_tmo .TP .B user_friendly_names .TP .B retain_attached_hw_handler .TP .B detect_prio .TP .B deferred_remove .TP .B delay_watch_checks .TP .B delay_wait_checks .RE .PD .LP .SH "WWID generation" Multipath uses a \fIWorld Wide Identification\fR (wwid) to determine which paths belong to the same device. Each path presenting the same wwid is assumed to point to the same device. .LP The wwid is generated by three methods (in the order of preference): .TP 17 .B getuid_callout Use the specified external program; cf \fIgetuid_callout\fR above. Care should be taken when using this method; the external program needs to be loaded from disk for execution, which might lead to deadlock situations in an all-paths-down scenario. .TP .B uid_attribute Use the value of the specified udev attribute; cf \fIuid_attribute\fR above. This method is preferred to \fIgetuid_callout\fR as multipath does not need to call any external programs here. However, under certain circumstances udev might not be able to generate the requested variable. .TP .B vpd_pg83 If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters are present multipath will try to use the sysfs attribute \fIvpd_pg83\fR to generate the wwid. .SH "KNOWN ISSUES" The usage of .B queue_if_no_path option can lead to .B D state processes being hung and not killable in situations where all the paths to the LUN go offline. It is advisable to use the .B no_path_retry option instead. .P The use of .B queue_if_no_path or .B no_path_retry might lead to a deadlock if the .B dev_loss_tmo setting results in a device being removed while I/O is still queued. The multipath daemon will update the .B dev_loss_tmo setting accordingly to avoid this deadlock. Hence if both values are specified the order of precedence is .I no_path_retry, queue_if_no_path, dev_loss_tmo .SH "SEE ALSO" .BR udev (8), .BR dmsetup (8) .BR multipath (8) .BR multipathd (8) .SH AUTHORS .B multipath was developed by Christophe Varoqui, and others. multipath-tools-0.5.0+git1.656f8865/multipath/multipath.init.suse000066400000000000000000000102421260771327300244310ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 2005 SuSE GmbH Nuernberg, Germany. # # Author: Hannes Reinecke # # init.d/boot.multipath # ### BEGIN INIT INFO # Provides: boot.multipath # Required-Start: boot.device-mapper boot.udev # Required-Stop: boot.device-mapper boot.udev # Should-Start: boot.xdrsetsite # Should-Stop: boot.xdrsetsite # Default-Start: B # Default-Stop: # Short-Description: Create multipath device targets # Description: Setup initial multipath device-mapper targets ### END INIT INFO PATH=/bin:/usr/bin:/sbin:/usr/sbin PROGRAM=/sbin/multipath # Set the maximum number of open files MAX_OPEN_FDS=4096 # Number of seconds to wait for disks and partitions MPATH_DEVICE_TIMEOUT=30 test -x $PROGRAM || exit 5 # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status # rc_status check and set local and overall rc status # rc_status -v ditto but be verbose in local rc status # rc_status -v -r ditto and clear the local rc status # rc_failed set local and overall rc status to failed # rc_reset clear local rc status (overall remains) # rc_exit exit appropriate to overall rc status . /etc/rc.status # First reset status of this service rc_reset # Return values acc. to LSB for all commands but status: # 0 - success # 1 - misc error # 2 - invalid or excess args # 3 - unimplemented feature (e.g. reload) # 4 - insufficient privilege # 5 - program not installed # 6 - program not configured # 7 - program is not running # # Note that starting an already running service, stopping # or restarting a not-running service as well as the restart # with force-reload (in case signalling is not supported) are # considered a success. case "$1" in start) # Check for existing multipath mappings if dmsetup table --target multipath | grep -q multipath ; then # Multipath active, start daemon exec /etc/init.d/multipathd $1 fi echo -n "Creating multipath targets:" # Check whether multipath daemon is already running if /sbin/multipathd -k"list paths" > /dev/null 2>&1 ; then echo -n " (multipathd running)" rc_status -v rc_exit fi # Load prerequisite module modprobe dm-multipath # Set the maximum number of open files if [ -n "$MAX_OPEN_FDS" ] ; then ulimit -n $MAX_OPEN_FDS fi # Start the program directly as checkproc doesn't work here $PROGRAM -v 0 echo -n " (waiting for udev)" # Wait for all multipathed devices to appear maplist=$(/sbin/dmsetup ls --target multipath | sed '/No devices/d' | sed -n 's/\(^[^ ()]*\)[\t ]*.*/\1/p') wait=$MPATH_DEVICE_TIMEOUT while [ $wait -gt 0 ] ; do num=0 for map in $maplist; do [ -e /dev/disk/by-id/dm-name-$map ] && continue num=$((num + 1)) done [ $num -eq 0 ] && break wait=$((wait - 1)) sleep 1; done if [ $wait -le 0 ] ; then echo -n " timeout: $num devices left" rc_failed 1 else # Reset to wait for partitions wait=$MPATH_DEVICE_TIMEOUT fi # Wait for all partitions on multipathed devices while [ $wait -gt 0 ] ; do num=0 for map in $maplist ; do [ -e /dev/disk/by-id/dm-name-$map ] || continue partlist=$(/sbin/kpartx -l -p _part /dev/disk/by-id/dm-name-$map | sed 's/\([^ ]*\) :.*/\1/p') for part in $partlist; do [ -e /dev/disk/by-id/dm-name-$part ] && continue num=$((num + 1)) done done [ $num -eq 0 ] && break wait=$((wait - 1)) sleep 1; done if [ $wait -le 0 ] ; then echo -n "timeout: $num partitions left" rc_failed 1 fi # Remember status and be verbose rc_status -v ;; stop) echo -n "Removing multipath targets:" # Flush all existing maps $PROGRAM -F rc_failed 0 rc_status -v ;; status) echo -n "Checking multipath targets: " # Display active multipath tables tblnum=$(/sbin/dmsetup ls --target multipath | sed '/No devices/d' | wc --lines) if [ "$tblnum" ] && [ $tblnum -gt 0 ] ; then echo -n "($tblnum multipath devices) " rc_failed 0 else rc_failed 3 fi rc_status -v ;; restart) $0 stop $0 start ;; *) echo "Usage: $0 {start|stop|status|restart}" exit 1 ;; esac rc_exit multipath-tools-0.5.0+git1.656f8865/multipath/multipath.rules000066400000000000000000000012721260771327300236450ustar00rootroot00000000000000# 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*", GOTO="end_mpath" ENV{DEVTYPE}!="partition", GOTO="test_dev" IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH" ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \ ENV{SYSTEMD_READY}="0" GOTO="end_mpath" 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}="none", \ ENV{SYSTEMD_READY}="0" LABEL="end_mpath" multipath-tools-0.5.0+git1.656f8865/multipathd/000077500000000000000000000000001260771327300207245ustar00rootroot00000000000000multipath-tools-0.5.0+git1.656f8865/multipathd/Makefile000066400000000000000000000026371260771327300223740ustar00rootroot00000000000000EXEC = multipathd include ../Makefile.inc # # basic flags setting # CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) endif LDFLAGS += -lpthread -ldevmapper -lreadline ifdef SYSTEMD ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) LDFLAGS += -lsystemd else LDFLAGS += -lsystemd-daemon endif endif LDFLAGS += -ludev -ldl \ -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist # # debuging stuff # #CFLAGS += -DLCKDBG #CFLAGS += -D_DEBUG_ #CFLAGS += -DLOGDBG # # object files # OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o # # directives # all : $(EXEC) $(EXEC): $(OBJS) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(rcdir) ifdef SYSTEMD $(INSTALL_PROGRAM) -d $(DESTDIR)$(unitdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).service $(DESTDIR)$(unitdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).socket $(DESTDIR)$(unitdir) endif $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) rm -f $(DESTDIR)$(rcdir)/$(EXEC) rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz rm -f $(DESTDIR)$(unitdir)/$(EXEC).service rm -f $(DESTDIR)$(unitdir)/$(EXEC).socket clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.5.0+git1.656f8865/multipathd/cli.c000066400000000000000000000272351260771327300216500ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include "cli.h" static vector keys; static vector handlers; static struct key * alloc_key (void) { return (struct key *)MALLOC(sizeof(struct key)); } static struct handler * alloc_handler (void) { return (struct handler *)MALLOC(sizeof(struct handler)); } static int add_key (vector vec, char * str, unsigned long code, int has_param) { struct key * kw; kw = alloc_key(); if (!kw) return 1; kw->code = code; kw->has_param = has_param; kw->str = STRDUP(str); if (!kw->str) goto out; if (!vector_alloc_slot(vec)) goto out1; vector_set_slot(vec, kw); return 0; out1: FREE(kw->str); out: FREE(kw); return 1; } int add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h; h = alloc_handler(); if (!h) return 1; if (!vector_alloc_slot(handlers)) { FREE(h); return 1; } vector_set_slot(handlers, h); h->fingerprint = fp; h->fn = fn; return 0; } static struct handler * find_handler (unsigned long fp) { int i; struct handler *h; vector_foreach_slot (handlers, h, i) if (h->fingerprint == fp) return h; return NULL; } int set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h = find_handler(fp); if (!h) return 1; h->fn = fn; return 0; } static void free_key (struct key * kw) { if (kw->str) FREE(kw->str); if (kw->param) FREE(kw->param); FREE(kw); } void free_keys (vector vec) { int i; struct key * kw; vector_foreach_slot (vec, kw, i) free_key(kw); vector_free(vec); } void free_handlers (void) { int i; struct handler * h; vector_foreach_slot (handlers, h, i) FREE(h); vector_free(handlers); handlers = NULL; } int load_keys (void) { int r = 0; keys = vector_alloc(); if (!keys) return 1; r += add_key(keys, "list", LIST, 0); r += add_key(keys, "show", LIST, 0); r += add_key(keys, "add", ADD, 0); r += add_key(keys, "remove", DEL, 0); r += add_key(keys, "del", DEL, 0); r += add_key(keys, "switch", SWITCH, 0); r += add_key(keys, "switchgroup", SWITCH, 0); r += add_key(keys, "suspend", SUSPEND, 0); r += add_key(keys, "resume", RESUME, 0); r += add_key(keys, "reinstate", REINSTATE, 0); r += add_key(keys, "fail", FAIL, 0); r += add_key(keys, "resize", RESIZE, 0); r += add_key(keys, "reset", RESET, 0); r += add_key(keys, "reload", RELOAD, 0); r += add_key(keys, "forcequeueing", FORCEQ, 0); r += add_key(keys, "disablequeueing", DISABLEQ, 0); r += add_key(keys, "restorequeueing", RESTOREQ, 0); r += add_key(keys, "paths", PATHS, 0); r += add_key(keys, "maps", MAPS, 0); r += add_key(keys, "multipaths", MAPS, 0); r += add_key(keys, "path", PATH, 1); r += add_key(keys, "map", MAP, 1); r += add_key(keys, "multipath", MAP, 1); r += add_key(keys, "group", GROUP, 1); r += add_key(keys, "reconfigure", RECONFIGURE, 0); r += add_key(keys, "daemon", DAEMON, 0); r += add_key(keys, "status", STATUS, 0); r += add_key(keys, "stats", STATS, 0); r += add_key(keys, "topology", TOPOLOGY, 0); r += add_key(keys, "config", CONFIG, 0); r += add_key(keys, "blacklist", BLACKLIST, 0); r += add_key(keys, "devices", DEVICES, 0); r += add_key(keys, "raw", RAW, 0); r += add_key(keys, "wildcards", WILDCARDS, 0); r += add_key(keys, "quit", QUIT, 0); r += add_key(keys, "exit", QUIT, 0); r += add_key(keys, "shutdown", SHUTDOWN, 0); r += add_key(keys, "getprstatus", GETPRSTATUS, 0); r += add_key(keys, "setprstatus", SETPRSTATUS, 0); r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0); r += add_key(keys, "format", FMT, 1); if (r) { free_keys(keys); keys = NULL; return 1; } return 0; } static struct key * find_key (const char * str) { int i; int len, klen; struct key * kw = NULL; struct key * foundkw = NULL; len = strlen(str); vector_foreach_slot (keys, kw, i) { if (strncmp(kw->str, str, len)) continue; klen = strlen(kw->str); if (len == klen) return kw; /* exact match */ if (len < klen) { if (!foundkw) foundkw = kw; /* shortcut match */ else return NULL; /* ambiguous word */ } } return foundkw; } #define E_SYNTAX 1 #define E_NOPARM 2 #define E_NOMEM 3 static int get_cmdvec (char * cmd, vector *v) { int i; int r = 0; int get_param = 0; char * buff; struct key * kw = NULL; struct key * cmdkw = NULL; vector cmdvec, strvec; strvec = alloc_strvec(cmd); if (!strvec) return E_NOMEM; cmdvec = vector_alloc(); if (!cmdvec) { free_strvec(strvec); return E_NOMEM; } vector_foreach_slot(strvec, buff, i) { if (*buff == '"') continue; if (get_param) { get_param = 0; cmdkw->param = strdup(buff); continue; } kw = find_key(buff); if (!kw) { r = E_SYNTAX; goto out; } cmdkw = alloc_key(); if (!cmdkw) { r = E_NOMEM; goto out; } if (!vector_alloc_slot(cmdvec)) { FREE(cmdkw); r = E_NOMEM; goto out; } vector_set_slot(cmdvec, cmdkw); cmdkw->code = kw->code; cmdkw->has_param = kw->has_param; if (kw->has_param) get_param = 1; } if (get_param) { r = E_NOPARM; goto out; } *v = cmdvec; free_strvec(strvec); return 0; out: free_strvec(strvec); free_keys(cmdvec); return r; } static unsigned long fingerprint(vector vec) { int i; unsigned long fp = 0; struct key * kw; if (!vec) return 0; vector_foreach_slot(vec, kw, i) fp += kw->code; return fp; } int alloc_handlers (void) { handlers = vector_alloc(); if (!handlers) return 1; return 0; } static int genhelp_sprint_aliases (char * reply, int maxlen, vector keys, struct key * refkw) { int i, len = 0; struct key * kw; vector_foreach_slot (keys, kw, i) { if (kw->code == refkw->code && kw != refkw) { len += snprintf(reply + len, maxlen - len, "|%s", kw->str); if (len >= maxlen) return len; } } return len; } static int do_genhelp(char *reply, int maxlen) { int len = 0; int i, j; unsigned long fp; struct handler * h; struct key * kw; len += snprintf(reply + len, maxlen - len, VERSION_STRING); if (len >= maxlen) goto out; len += snprintf(reply + len, maxlen - len, "CLI commands reference:\n"); if (len >= maxlen) goto out; vector_foreach_slot (handlers, h, i) { fp = h->fingerprint; vector_foreach_slot (keys, kw, j) { if ((kw->code & fp)) { fp -= kw->code; len += snprintf(reply + len , maxlen - len, " %s", kw->str); if (len >= maxlen) goto out; len += genhelp_sprint_aliases(reply + len, maxlen - len, keys, kw); if (len >= maxlen) goto out; if (kw->has_param) { len += snprintf(reply + len, maxlen - len, " $%s", kw->str); if (len >= maxlen) goto out; } } } len += snprintf(reply + len, maxlen - len, "\n"); if (len >= maxlen) goto out; } out: return len; } static char * genhelp_handler (void) { char * reply; char * p = NULL; int maxlen = INITIAL_REPLY_LEN; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) return NULL; p = reply; p += do_genhelp(reply, maxlen); again = ((p - reply) >= maxlen); REALLOC_REPLY(reply, again, maxlen); } return reply; } int parse_cmd (char * cmd, char ** reply, int * len, void * data) { int r; struct handler * h; vector cmdvec = NULL; r = get_cmdvec(cmd, &cmdvec); if (r) { *reply = genhelp_handler(); *len = strlen(*reply) + 1; return 0; } h = find_handler(fingerprint(cmdvec)); if (!h || !h->fn) { *reply = genhelp_handler(); *len = strlen(*reply) + 1; free_keys(cmdvec); return 0; } /* * execute handler */ r = h->fn(cmdvec, reply, len, data); free_keys(cmdvec); return r; } char * get_keyparam (vector v, unsigned long code) { struct key * kw; int i; vector_foreach_slot(v, kw, i) if (kw->code == code) return kw->param; return NULL; } int cli_init (void) { if (load_keys()) return 1; if (alloc_handlers()) return 1; add_handler(LIST+PATHS, NULL); add_handler(LIST+PATHS+FMT, NULL); add_handler(LIST+PATHS+RAW+FMT, NULL); add_handler(LIST+PATH, NULL); add_handler(LIST+STATUS, NULL); add_handler(LIST+DAEMON, NULL); add_handler(LIST+MAPS, NULL); add_handler(LIST+MAPS+STATUS, NULL); add_handler(LIST+MAPS+STATS, NULL); add_handler(LIST+MAPS+FMT, NULL); add_handler(LIST+MAPS+RAW+FMT, NULL); add_handler(LIST+MAPS+TOPOLOGY, NULL); add_handler(LIST+TOPOLOGY, NULL); add_handler(LIST+MAP+TOPOLOGY, NULL); add_handler(LIST+CONFIG, NULL); add_handler(LIST+BLACKLIST, NULL); add_handler(LIST+DEVICES, NULL); add_handler(LIST+WILDCARDS, NULL); add_handler(ADD+PATH, NULL); add_handler(DEL+PATH, NULL); add_handler(ADD+MAP, NULL); add_handler(DEL+MAP, NULL); add_handler(SWITCH+MAP+GROUP, NULL); add_handler(RECONFIGURE, NULL); add_handler(SUSPEND+MAP, NULL); add_handler(RESUME+MAP, NULL); add_handler(RESIZE+MAP, NULL); add_handler(RESET+MAP, NULL); add_handler(RELOAD+MAP, NULL); add_handler(DISABLEQ+MAP, NULL); add_handler(RESTOREQ+MAP, NULL); add_handler(DISABLEQ+MAPS, NULL); add_handler(RESTOREQ+MAPS, NULL); add_handler(REINSTATE+PATH, NULL); add_handler(FAIL+PATH, NULL); add_handler(QUIT, NULL); add_handler(SHUTDOWN, NULL); add_handler(GETPRSTATUS+MAP, NULL); add_handler(SETPRSTATUS+MAP, NULL); add_handler(UNSETPRSTATUS+MAP, NULL); add_handler(FORCEQ+DAEMON, NULL); add_handler(RESTOREQ+DAEMON, NULL); return 0; } void cli_exit(void) { free_handlers(); free_keys(keys); keys = NULL; } static int key_match_fingerprint (struct key * kw, unsigned long fp) { if (!fp) return 0; return ((fp & kw->code) == kw->code); } /* * This is the readline completion handler */ char * key_generator (const char * str, int state) { static int index, len, has_param; static unsigned long rlfp; struct key * kw; int i; struct handler *h; vector v = NULL; if (!state) { index = 0; has_param = 0; rlfp = 0; len = strlen(str); int r = get_cmdvec(rl_line_buffer, &v); /* * If a word completion is in progess, we don't want * to take an exact keyword match in the fingerprint. * For ex "show map[tab]" would validate "map" and discard * "maps" as a valid candidate. */ if (v && len) vector_del_slot(v, VECTOR_SIZE(v) - 1); /* * Clean up the mess if we dropped the last slot of a 1-slot * vector */ if (v && !VECTOR_SIZE(v)) { vector_free(v); v = NULL; } /* * If last keyword takes a param, don't even try to guess */ if (r == E_NOPARM) { has_param = 1; return (strdup("(value)")); } /* * Compute a command fingerprint to find out possible completions. * Once done, the vector is useless. Free it. */ if (v) { rlfp = fingerprint(v); free_keys(v); } } /* * No more completions for parameter placeholder. * Brave souls might try to add parameter completion by walking paths and * multipaths vectors. */ if (has_param) return ((char *)NULL); /* * Loop through keywords for completion candidates */ vector_foreach_slot_after (keys, kw, index) { if (!strncmp(kw->str, str, len)) { /* * Discard keywords already in the command line */ if (key_match_fingerprint(kw, rlfp)) { struct key * curkw = find_key(str); if (!curkw || (curkw != kw)) continue; } /* * Discard keywords making syntax errors. * * nfp is the candidate fingerprint we try to * validate against all known command fingerprints. */ unsigned long nfp = rlfp | kw->code; vector_foreach_slot(handlers, h, i) { if (!rlfp || ((h->fingerprint & nfp) == nfp)) { /* * At least one full command is * possible with this keyword : * Consider it validated */ index++; return (strdup(kw->str)); } } } } /* * No more candidates */ return ((char *)NULL); } multipath-tools-0.5.0+git1.656f8865/multipathd/cli.h000066400000000000000000000047471260771327300216600ustar00rootroot00000000000000enum { __LIST, __ADD, __DEL, __SWITCH, __SUSPEND, __RESUME, __REINSTATE, __FAIL, __RESIZE, __RESET, __RELOAD, __FORCEQ, __DISABLEQ, __RESTOREQ, __PATHS, __MAPS, __PATH, __MAP, __GROUP, __RECONFIGURE, __DAEMON, __STATUS, __STATS, __TOPOLOGY, __CONFIG, __BLACKLIST, __DEVICES, __RAW, __WILDCARDS, __QUIT, __SHUTDOWN, __GETPRSTATUS, __SETPRSTATUS, __UNSETPRSTATUS, __FMT, }; #define LIST (1 << __LIST) #define ADD (1 << __ADD) #define DEL (1 << __DEL) #define SWITCH (1 << __SWITCH) #define SUSPEND (1 << __SUSPEND) #define RESUME (1 << __RESUME) #define REINSTATE (1 << __REINSTATE) #define FAIL (1 << __FAIL) #define RESIZE (1 << __RESIZE) #define RESET (1 << __RESET) #define RELOAD (1 << __RELOAD) #define FORCEQ (1 << __FORCEQ) #define DISABLEQ (1 << __DISABLEQ) #define RESTOREQ (1 << __RESTOREQ) #define PATHS (1 << __PATHS) #define MAPS (1 << __MAPS) #define PATH (1 << __PATH) #define MAP (1 << __MAP) #define GROUP (1 << __GROUP) #define RECONFIGURE (1 << __RECONFIGURE) #define DAEMON (1 << __DAEMON) #define STATUS (1 << __STATUS) #define STATS (1 << __STATS) #define TOPOLOGY (1 << __TOPOLOGY) #define CONFIG (1 << __CONFIG) #define BLACKLIST (1 << __BLACKLIST) #define DEVICES (1 << __DEVICES) #define RAW (1 << __RAW) #define COUNT (1 << __COUNT) #define WILDCARDS (1 << __WILDCARDS) #define QUIT (1 << __QUIT) #define SHUTDOWN (1 << __SHUTDOWN) #define GETPRSTATUS (1UL << __GETPRSTATUS) #define SETPRSTATUS (1UL << __SETPRSTATUS) #define UNSETPRSTATUS (1UL << __UNSETPRSTATUS) #define FMT (1UL << __FMT) #define INITIAL_REPLY_LEN 1200 #define REALLOC_REPLY(r, a, m) \ do { \ if ((a)) { \ char *tmp = (r); \ (r) = REALLOC((r), (m) * 2); \ if ((r)) { \ memset((r) + (m), 0, (m)); \ (m) *= 2; \ } \ else \ free(tmp); \ } \ } while (0) struct key { char * str; char * param; unsigned long code; int has_param; }; struct handler { unsigned long fingerprint; int (*fn)(void *, char **, int *, void *); }; int alloc_handlers (void); int add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *)); int set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *)); int parse_cmd (char * cmd, char ** reply, int * len, void *); int load_keys (void); char * get_keyparam (vector v, unsigned long code); void free_keys (vector vec); void free_handlers (void); int cli_init (void); void cli_exit(void); char * key_generator (const char * str, int state); multipath-tools-0.5.0+git1.656f8865/multipathd/cli_handlers.c000066400000000000000000000560461260771327300235320ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "cli.h" #include "uevent.h" int show_paths (char ** r, int * len, struct vectors * vecs, char * style, int pretty) { int i; struct path * pp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; if (pretty && VECTOR_SIZE(vecs->pathvec) > 0) c += snprint_path_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->pathvec, pp, i) c += snprint_path(c, reply + maxlen - c, style, pp, pretty); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_path (char ** r, int * len, struct vectors * vecs, struct path *pp, char * style) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_path(c, reply + maxlen - c, style, pp, 0); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_map_topology (char ** r, int * len, struct multipath * mpp, struct vectors * vecs) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; if (update_multipath(vecs, mpp->alias, 0)) return 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_maps_topology (char ** r, int * len, struct vectors * vecs) { int i; struct multipath * mpp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 0); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { i--; continue; } c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); } again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_config (char ** r, int * len) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; c = reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_defaults(c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_blacklist(c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_blacklist_except(c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_overrides(c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; if (VECTOR_SIZE(conf->mptable) > 0) { c += snprint_mptable(c, reply + maxlen - c, conf->mptable); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } } *r = reply; *len = (int)(c - reply + 1); return 0; } int cli_list_config (void * v, char ** reply, int * len, void * data) { condlog(3, "list config (operator)"); return show_config(reply, len); } int cli_list_paths (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1); } int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, fmt, 1); } int cli_list_paths_raw (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, fmt, 0); } int cli_list_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; param = convert_dev(param, 1); condlog(3, "%s: list path (operator)", param); pp = find_path_by_dev(vecs->pathvec, param); return show_path(reply, len, vecs, pp, "%o"); } int cli_list_map_topology (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list multipath %s (operator)", param); return show_map_topology(reply, len, mpp, vecs); } int cli_list_maps_topology (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list multipaths (operator)"); return show_maps_topology(reply, len, vecs); } int cli_list_wildcards (void * v, char ** reply, int * len, void * data) { char * c; *reply = MALLOC(INITIAL_REPLY_LEN); if (!*reply) return 1; c = *reply; c += snprint_wildcards(c, INITIAL_REPLY_LEN); *len = INITIAL_REPLY_LEN; return 0; } int show_status (char ** r, int *len, struct vectors * vecs) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprint_status(c, reply + maxlen - c, vecs); *r = reply; *len = (int)(c - reply + 1); return 0; } int show_daemon (char ** r, int *len) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprintf(c, INITIAL_REPLY_LEN, "pid %d %s\n", daemon_pid, daemon_status()); *r = reply; *len = (int)(c - reply + 1); return 0; } int show_maps (char ** r, int *len, struct vectors * vecs, char * style, int pretty) { int i; struct multipath * mpp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_multipath_layout(vecs->mpvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; if (pretty && VECTOR_SIZE(vecs->mpvec) > 0) c += snprint_multipath_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->mpvec, mpp, i) c += snprint_multipath(c, reply + maxlen - c, style, mpp, pretty); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, fmt, 1); } int cli_list_maps_raw (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, fmt, 0); } int cli_list_maps (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1); } int cli_list_status (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list status (operator)"); return show_status(reply, len, vecs); } int cli_list_maps_status (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps status (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1); } int cli_list_maps_stats (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps stats (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1); } int cli_list_daemon (void * v, char ** reply, int * len, void * data) { condlog(3, "list daemon (operator)"); return show_daemon(reply, len); } int cli_add_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; int r; param = convert_dev(param, 1); condlog(2, "%s: add path (operator)", param); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, param) > 0) goto blacklisted; pp = find_path_by_dev(vecs->pathvec, param); if (pp) { condlog(2, "%s: path already in pathvec", param); if (pp->mpp) return 0; } else { struct udev_device *udevice; udevice = udev_device_new_from_subsystem_sysname(conf->udev, "block", param); r = store_pathinfo(vecs->pathvec, conf->hwtable, udevice, DI_ALL, &pp); udev_device_unref(udevice); if (!pp) { if (r == 2) goto blacklisted; condlog(0, "%s: failed to store path info", param); return 1; } pp->checkint = conf->checkint; } return ev_add_path(pp, vecs); blacklisted: *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: path blacklisted", param); return 0; } int cli_del_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; param = convert_dev(param, 1); condlog(2, "%s: remove path (operator)", param); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) { condlog(0, "%s: path already removed", param); return 0; } return ev_remove_path(pp, vecs); } int cli_add_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int major, minor; char dev_path[PATH_SIZE]; char *alias, *refwwid; int rc, count = 0; param = convert_dev(param, 0); condlog(2, "%s: add map (operator)", param); if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) { *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 1; } do { minor = dm_get_minor(param); if (minor < 0) condlog(2, "%s: not a device mapper table", param); major = dm_get_major(param); if (major < 0) condlog(2, "%s: not a device mapper table", param); sprintf(dev_path, "dm-%d", minor); alias = dm_mapname(major, minor); /*if there is no mapname found, we first create the device*/ if (!alias && !count) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); rc = get_refwwid(param, DEV_DEVMAP, vecs->pathvec, &refwwid); if (refwwid) { if (coalesce_paths(vecs, NULL, refwwid, 0)) condlog(2, "%s: coalesce_paths failed", param); dm_lib_release(); } } /*we attempt to create device only once*/ count++; } while (!alias && (count < 2)); if (!alias) { condlog(2, "%s: add map failed", param); return 1; } rc = ev_add_map(dev_path, alias, vecs); FREE(alias); FREE(refwwid); return rc; } int cli_del_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int major, minor; char dev_path[PATH_SIZE]; char *alias; int rc; param = convert_dev(param, 0); condlog(2, "%s: remove map (operator)", param); minor = dm_get_minor(param); if (minor < 0) { condlog(2, "%s: not a device mapper table", param); return 0; } major = dm_get_major(param); if (major < 0) { condlog(2, "%s: not a device mapper table", param); return 0; } sprintf(dev_path,"dm-%d", minor); alias = dm_mapname(major, minor); if (!alias) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); return 0; } rc = ev_remove_map(param, alias, minor, vecs); FREE(alias); return rc; } int cli_reload(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: reload map (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name. cannot reload", mapname); return 1; } return reload_map(vecs, mpp, 0); } int resize_map(struct multipath *mpp, unsigned long long size, struct vectors * vecs) { char params[PARAMS_SIZE] = {0}; unsigned long long orig_size = mpp->size; mpp->size = size; update_mpp_paths(mpp, vecs->pathvec); setup_map(mpp, params, PARAMS_SIZE); mpp->action = ACT_RESIZE; if (domap(mpp, params) <= 0) { condlog(0, "%s: failed to resize map : %s", mpp->alias, strerror(errno)); mpp->size = orig_size; return 1; } return 0; } int cli_resize(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; unsigned long long size; struct pathgroup *pgp; struct path *pp; mapname = convert_dev(mapname, 0); condlog(2, "%s: resize map (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name. cannot resize", mapname); return 1; } pgp = VECTOR_SLOT(mpp->pg, 0); if (!pgp){ condlog(0, "%s: couldn't get path group. cannot resize", mapname); return 1; } pp = VECTOR_SLOT(pgp->paths, 0); if (!pp){ condlog(0, "%s: couldn't get path. cannot resize", mapname); return 1; } if (!pp->udev || sysfs_get_size(pp, &size)) { condlog(0, "%s: couldn't get size for sysfs. cannot resize", mapname); return 1; } if (size == mpp->size) { condlog(0, "%s: map is still the same size (%llu)", mapname, mpp->size); return 0; } condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size, size); if (resize_map(mpp, size, vecs) != 0) return 1; dm_lib_release(); if (setup_multipath(vecs, mpp) != 0) return 1; sync_map_state(mpp); return 0; } int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) { condlog(2, "force queue_without_daemon (operator)"); if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) conf->queue_without_daemon = QUE_NO_DAEMON_FORCE; return 0; } int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data) { condlog(2, "restore queue_without_daemon (operator)"); if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE) conf->queue_without_daemon = QUE_NO_DAEMON_OFF; return 0; } int cli_restore_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: restore map queueing (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name, cannot restore queueing", mapname); return 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); if (mpp->nr_active > 0) mpp->retry_tick = 0; else mpp->retry_tick = mpp->no_path_retry * conf->checkint; } return 0; } int cli_restore_all_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; int i; condlog(2, "restore queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { 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); if (mpp->nr_active > 0) mpp->retry_tick = 0; else mpp->retry_tick = mpp->no_path_retry * conf->checkint; } } return 0; } int cli_disable_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: disable map queueing (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name, cannot disable queueing", mapname); return 1; } mpp->retry_tick = 0; dm_queue_if_no_path(mpp->alias, 0); return 0; } int cli_disable_all_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; int i; condlog(2, "disable queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { mpp->retry_tick = 0; dm_queue_if_no_path(mpp->alias, 0); } return 0; } int cli_switch_group(void * v, char ** reply, int * len, void * data) { char * mapname = get_keyparam(v, MAP); int groupnum = atoi(get_keyparam(v, GROUP)); mapname = convert_dev(mapname, 0); condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum); return dm_switchgroup(mapname, groupnum); } int cli_reconfigure(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(2, "reconfigure (operator)"); return reconfigure(vecs); } int cli_suspend(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0, 0); param = convert_dev(param, 0); condlog(2, "%s: suspend (operator)", param); if (!r) /* error */ return 1; struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param); if (!mpp) return 1; dm_get_info(param, &mpp->dmi); return 0; } int cli_resume(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, 0, 0); param = convert_dev(param, 0); condlog(2, "%s: resume (operator)", param); if (!r) /* error */ return 1; struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param); if (!mpp) return 1; dm_get_info(param, &mpp->dmi); return 0; } int cli_reinstate(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path * pp; param = convert_dev(param, 1); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) pp = find_path_by_devt(vecs->pathvec, param); if (!pp || !pp->mpp || !pp->mpp->alias) return 1; condlog(2, "%s: reinstate path %s (operator)", pp->mpp->alias, pp->dev_t); checker_enable(&pp->checker); return dm_reinstate_path(pp->mpp->alias, pp->dev_t); } int cli_reassign (void * v, char ** reply, int * len, void * data) { char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); condlog(3, "%s: reset devices (operator)", param); dm_reassign(param); return 0; } int cli_fail(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path * pp; int r; param = convert_dev(param, 1); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) pp = find_path_by_devt(vecs->pathvec, param); if (!pp || !pp->mpp || !pp->mpp->alias) return 1; condlog(2, "%s: fail path %s (operator)", pp->mpp->alias, pp->dev_t); r = dm_fail_path(pp->mpp->alias, pp->dev_t); /* * Suspend path checking to avoid auto-reinstating the path */ if (!r) checker_disable(&pp->checker); return r; } int show_blacklist (char ** r, int * len) { char *c = NULL; char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_blacklist_report(c, maxlen); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int cli_list_blacklist (void * v, char ** reply, int * len, void * data) { condlog(3, "list blacklist (operator)"); return show_blacklist(reply, len); } int show_devices (char ** r, int * len, struct vectors *vecs) { char *c = NULL; char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_devices(c, maxlen, vecs); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int cli_list_devices (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list devices (operator)"); return show_devices(reply, len, vecs); } int cli_quit (void * v, char ** reply, int * len, void * data) { return 0; } int cli_shutdown (void * v, char ** reply, int * len, void * data) { condlog(3, "shutdown (operator)"); exit_daemon(); return 0; } int cli_getprstatus (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag); *reply =(char *)malloc(2); *len = 2; memset(*reply,0,2); sprintf(*reply,"%d",mpp->prflag); (*reply)[1]='\0'; condlog(3, "%s: reply = %s", param, *reply); return 0; } int cli_setprstatus(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; if (!mpp->prflag) { mpp->prflag = 1; condlog(2, "%s: prflag set", param); } return 0; } int cli_unsetprstatus(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; if (mpp->prflag) { mpp->prflag = 0; condlog(2, "%s: prflag unset", param); } return 0; } multipath-tools-0.5.0+git1.656f8865/multipathd/cli_handlers.h000066400000000000000000000055451260771327300235350ustar00rootroot00000000000000int cli_list_paths (void * v, char ** reply, int * len, void * data); int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data); int cli_list_paths_raw (void * v, char ** reply, int * len, void * data); int cli_list_path (void * v, char ** reply, int * len, void * data); int cli_list_status (void * v, char ** reply, int * len, void * data); int cli_list_daemon (void * v, char ** reply, int * len, void * data); int cli_list_maps (void * v, char ** reply, int * len, void * data); int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data); int cli_list_maps_raw (void * v, char ** reply, int * len, void * data); int cli_list_maps_status (void * v, char ** reply, int * len, void * data); int cli_list_maps_stats (void * v, char ** reply, int * len, void * data); int cli_list_map_topology (void * v, char ** reply, int * len, void * data); int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); int cli_list_config (void * v, char ** reply, int * len, void * data); int cli_list_blacklist (void * v, char ** reply, int * len, void * data); int cli_list_devices (void * v, char ** reply, int * len, void * data); int cli_list_wildcards (void * v, char ** reply, int * len, void * data); int cli_add_path (void * v, char ** reply, int * len, void * data); int cli_del_path (void * v, char ** reply, int * len, void * data); int cli_add_map (void * v, char ** reply, int * len, void * data); int cli_del_map (void * v, char ** reply, int * len, void * data); int cli_switch_group(void * v, char ** reply, int * len, void * data); int cli_reconfigure(void * v, char ** reply, int * len, void * data); int cli_resize(void * v, char ** reply, int * len, void * data); int cli_reload(void * v, char ** reply, int * len, void * data); int cli_disable_queueing(void * v, char ** reply, int * len, void * data); int cli_disable_all_queueing(void * v, char ** reply, int * len, void * data); int cli_restore_queueing(void * v, char ** reply, int * len, void * data); int cli_restore_all_queueing(void * v, char ** reply, int * len, void * data); int cli_suspend(void * v, char ** reply, int * len, void * data); int cli_resume(void * v, char ** reply, int * len, void * data); int cli_reinstate(void * v, char ** reply, int * len, void * data); int cli_fail(void * v, char ** reply, int * len, void * data); int 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); int cli_quit(void * v, char ** reply, int * len, void * data); int cli_shutdown(void * v, char ** reply, int * len, void * data); int cli_reassign (void * v, char ** reply, int * len, void * data); int cli_getprstatus(void * v, char ** reply, int * len, void * data); int cli_setprstatus(void * v, char ** reply, int * len, void * data); int cli_unsetprstatus(void * v, char ** reply, int * len, void * data); multipath-tools-0.5.0+git1.656f8865/multipathd/main.c000066400000000000000000001371061260771327300220240ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #ifdef USE_SYSTEMD #include #endif #include #include #include /* * libcheckers */ #include #ifdef USE_SYSTEMD static int use_watchdog; #endif /* * libmultipath */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "pidfile.h" #include "uxlsnr.h" #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" #include "lock.h" #include "waiter.h" #include "wwids.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 #define LOG_MSG(a, b) \ do { \ if (pp->offline) \ condlog(a, "%s: %s - path offline", pp->mpp->alias, pp->dev); \ else if (strlen(b)) \ condlog(a, "%s: %s - %s", pp->mpp->alias, pp->dev, b); \ } while(0) struct mpath_event_param { char * devname; struct multipath *mpp; }; unsigned int mpath_mx_alloc_len; int logsink; enum daemon_status running_state; pid_t daemon_pid; static sem_t exit_sem; /* * global copy of vecs for use in sig handlers */ struct vectors * gvecs; struct udev * udev; static int need_switch_pathgroup (struct multipath * mpp, int refresh) { struct pathgroup * pgp; struct path * pp; unsigned int i, j; if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL) return 0; /* * Refresh path priority values */ if (refresh) vector_foreach_slot (mpp->pg, pgp, i) vector_foreach_slot (pgp->paths, pp, j) pathinfo(pp, conf->hwtable, DI_PRIO); mpp->bestpg = select_path_group(mpp); if (mpp->bestpg != mpp->nextpg) return 1; return 0; } static void switch_pathgroup (struct multipath * mpp) { mpp->stat_switchgroup++; dm_switchgroup(mpp->alias, mpp->bestpg); condlog(2, "%s: switch to path group #%i", mpp->alias, mpp->bestpg); } static int coalesce_maps(struct vectors *vecs, vector nmpv) { struct multipath * ompp; vector ompv = vecs->mpvec; unsigned int i; vector_foreach_slot (ompv, ompp, i) { condlog(3, "%s: coalesce map", ompp->alias); if (!find_mp_by_wwid(nmpv, ompp->wwid)) { /* * remove all current maps not allowed by the * current configuration */ if (dm_flush_map(ompp->alias)) { condlog(0, "%s: unable to flush devmap", ompp->alias); /* * may be just because the device is open */ if (setup_multipath(vecs, ompp) != 0) { i--; continue; } if (!vector_alloc_slot(nmpv)) return 1; vector_set_slot(nmpv, ompp); vector_del_slot(ompv, i); i--; } else { dm_lib_release(); condlog(2, "%s devmap removed", ompp->alias); } } else if (conf->reassign_maps) { condlog(3, "%s: Reassign existing device-mapper" " devices", ompp->alias); dm_reassign(ompp->alias); } } return 0; } void sync_map_state(struct multipath *mpp) { struct pathgroup *pgp; struct path *pp; unsigned int i, j; if (!mpp->pg) return; vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD || pp->state == PATH_DELAYED) continue; if ((pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_UP || pp->state == PATH_GHOST)) dm_reinstate_path(mpp->alias, pp->dev_t); else if ((pp->dmstate == PSTATE_ACTIVE || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_DOWN || pp->state == PATH_SHAKY)) dm_fail_path(mpp->alias, pp->dev_t); } } } static void sync_maps_state(vector mpvec) { unsigned int i; struct multipath *mpp; vector_foreach_slot (mpvec, mpp, i) sync_map_state(mpp); } static int flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) { int r; if (nopaths) r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); else r = dm_flush_map(mpp->alias); /* * clear references to this map before flushing so we can ignore * the spurious uevent we may generate with the dm_flush_map call below */ if (r) { /* * May not really be an error -- if the map was already flushed * from the device mapper by dmsetup(8) for instance. */ if (r == 1) condlog(0, "%s: can't flush", mpp->alias); else { condlog(2, "%s: devmap deferred remove", mpp->alias); mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS; } return r; } else { dm_lib_release(); condlog(2, "%s: map flushed", mpp->alias); } orphan_paths(vecs->pathvec, mpp); remove_map_and_stop_waiter(mpp, vecs, 1); return 0; } static int uev_add_map (struct uevent * uev, struct vectors * vecs) { char *alias; int major = -1, minor = -1, rc; condlog(3, "%s: add map (uevent)", uev->kernel); alias = uevent_get_dm_name(uev); if (!alias) { condlog(3, "%s: No DM_NAME in uevent", uev->kernel); major = uevent_get_major(uev); minor = uevent_get_minor(uev); alias = dm_mapname(major, minor); if (!alias) { condlog(2, "%s: mapname not found for %d:%d", uev->kernel, major, minor); return 1; } } rc = ev_add_map(uev->kernel, alias, vecs); FREE(alias); return rc; } int ev_add_map (char * dev, char * alias, struct vectors * vecs) { char * refwwid; struct multipath * mpp; int map_present; int r = 1; map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)) { condlog(4, "%s: not a multipath map", alias); return 0; } mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { /* * Not really an error -- we generate our own uevent * if we create a multipath mapped device as a result * of uev_add_path */ if (conf->reassign_maps) { condlog(3, "%s: Reassign existing device-mapper devices", alias); dm_reassign(alias); } return 0; } condlog(2, "%s: adding map", alias); /* * now we can register the map */ if (map_present) { if ((mpp = add_map_without_path(vecs, alias))) { sync_map_state(mpp); condlog(2, "%s: devmap %s registered", alias, dev); return 0; } else { condlog(2, "%s: uev_add_map failed", dev); return 1; } } r = get_refwwid(dev, DEV_DEVMAP, vecs->pathvec, &refwwid); if (refwwid) { r = coalesce_paths(vecs, NULL, refwwid, 0); dm_lib_release(); } if (!r) condlog(2, "%s: devmap %s added", alias, dev); else if (r == 2) condlog(2, "%s: uev_add_map %s blacklisted", alias, dev); else condlog(0, "%s: uev_add_map %s failed", alias, dev); FREE(refwwid); return r; } static int uev_remove_map (struct uevent * uev, struct vectors * vecs) { char *alias; int minor; struct multipath *mpp; condlog(2, "%s: remove map (uevent)", uev->kernel); alias = uevent_get_dm_name(uev); if (!alias) { condlog(3, "%s: No DM_NAME in uevent, ignoring", uev->kernel); return 0; } minor = uevent_get_minor(uev); mpp = find_mp_by_minor(vecs->mpvec, minor); if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", uev->kernel); goto out; } if (strcmp(mpp->alias, alias)) { condlog(2, "%s: minor number mismatch (map %d, event %d)", mpp->alias, mpp->dmi->minor, minor); goto out; } orphan_paths(vecs->pathvec, mpp); remove_map_and_stop_waiter(mpp, vecs, 1); out: FREE(alias); return 0; } int ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) { struct multipath * mpp; mpp = find_mp_by_minor(vecs->mpvec, minor); if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", devname); return 0; } if (strcmp(mpp->alias, alias)) { condlog(2, "%s: minor number mismatch (map %d, event %d)", mpp->alias, mpp->dmi->minor, minor); return 0; } return flush_map(mpp, vecs, 0); } static int uev_add_path (struct uevent *uev, struct vectors * vecs) { struct path *pp; int ret = 0, i; condlog(2, "%s: add path (uevent)", uev->kernel); if (strstr(uev->kernel, "..") != NULL) { /* * Don't allow relative device names in the pathvec */ condlog(0, "%s: path name is invalid", uev->kernel); return 1; } pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (pp) { int r; condlog(0, "%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); r = pathinfo(pp, conf->hwtable, DI_ALL | DI_BLACKLIST); if (r == PATHINFO_OK) ret = ev_add_path(pp, vecs); else if (r == PATHINFO_SKIPPED) { condlog(3, "%s: remove blacklisted path", uev->kernel); i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); } else { condlog(0, "%s: failed to reinitialize path", uev->kernel); ret = 1; } } return ret; } /* * get path vital state */ ret = alloc_path_with_pathinfo(conf->hwtable, uev->udev, DI_ALL, &pp); if (!pp) { if (ret == PATHINFO_SKIPPED) return 0; condlog(3, "%s: failed to get path info", uev->kernel); return 1; } if (!strlen(pp->wwid)) { condlog(3, "%s: Failed to get path wwid", uev->kernel); free_path(pp); return 1; } ret = store_path(vecs->pathvec, pp); if (!ret) { pp->checkint = conf->checkint; ret = ev_add_path(pp, vecs); } else { condlog(0, "%s: failed to store path info, " "dropping event", uev->kernel); free_path(pp); ret = 1; } return ret; } /* * returns: * 0: added * 1: error */ int ev_add_path (struct path * pp, struct vectors * vecs) { struct multipath * mpp; char params[PARAMS_SIZE] = {0}; int retries = 3; int start_waiter = 0; int ret; /* * need path UID to go any further */ if (strlen(pp->wwid) == 0) { condlog(0, "%s: failed to get path uid", pp->dev); goto fail; /* leave path added to pathvec */ } mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); rescan: if (mpp) { if (mpp->size != pp->size) { condlog(0, "%s: failed to add new path %s, " "device size mismatch", mpp->alias, pp->dev); int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return 1; } condlog(4,"%s: adopting all paths for path %s", mpp->alias, pp->dev); if (adopt_paths(vecs->pathvec, mpp, 1)) goto fail; /* leave path added to pathvec */ verify_paths(mpp, vecs); mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; } else { if (conf->find_multipaths && !should_multipath(pp, vecs->pathvec)) { orphan_path(pp, "only one path"); return 0; } condlog(4,"%s: creating new map", pp->dev); if ((mpp = add_map_with_path(vecs, pp, 1))) { mpp->action = ACT_CREATE; /* * We don't depend on ACT_CREATE, as domap will * set it to ACT_NOTHING when complete. */ start_waiter = 1; } else goto fail; /* leave path added to pathvec */ } /* persistent reseravtion check*/ mpath_pr_event_handle(pp); /* * push the map to the device-mapper */ if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map for addition of new " "path %s", mpp->alias, pp->dev); goto fail_map; } /* * reload the map for the multipath mapped device */ retry: ret = domap(mpp, params); if (ret <= 0) { if (ret < 0 && retries-- > 0) { condlog(0, "%s: retry domap for addition of new " "path %s", mpp->alias, pp->dev); sleep(1); goto retry; } condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, pp->dev); /* * deal with asynchronous uevents :(( */ if (mpp->action == ACT_RELOAD && retries-- > 0) { condlog(0, "%s: uev_add_path sleep", mpp->alias); sleep(1); update_mpp_paths(mpp, vecs->pathvec); goto rescan; } else if (mpp->action == ACT_RELOAD) condlog(0, "%s: giving up reload", mpp->alias); else goto fail_map; } dm_lib_release(); /* * update our state from kernel regardless of create or reload */ if (setup_multipath(vecs, mpp)) goto fail; /* if setup_multipath fails, it removes the map */ 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); return 0; } else goto fail; fail_map: remove_map(mpp, vecs, 1); fail: orphan_path(pp, "failed to add path"); return 1; } static int uev_remove_path (struct uevent *uev, struct vectors * vecs) { struct path *pp; condlog(2, "%s: remove path (uevent)", uev->kernel); pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (!pp) { /* Not an error; path might have been purged earlier */ condlog(0, "%s: path already removed", uev->kernel); return 0; } return ev_remove_path(pp, vecs); } int ev_remove_path (struct path *pp, struct vectors * vecs) { struct multipath * mpp; int i, retval = 0; char params[PARAMS_SIZE] = {0}; /* * avoid referring to the map of an orphaned path */ if ((mpp = pp->mpp)) { /* * transform the mp->pg vector of vectors of paths * into a mp->params string to feed the device-mapper */ if (update_mpp_paths(mpp, vecs->pathvec)) { condlog(0, "%s: failed to update paths", mpp->alias); goto fail; } if ((i = find_slot(mpp->paths, (void *)pp)) != -1) vector_del_slot(mpp->paths, i); /* * remove the map IFF removing the last path */ if (VECTOR_SIZE(mpp->paths) == 0) { char alias[WWID_SIZE]; /* * flush_map will fail if the device is open */ strncpy(alias, mpp->alias, WWID_SIZE); if (mpp->flush_on_last_del == FLUSH_ENABLED) { condlog(2, "%s Last path deleted, disabling queueing", mpp->alias); mpp->retry_tick = 0; mpp->no_path_retry = NO_PATH_RETRY_FAIL; mpp->flush_on_last_del = FLUSH_IN_PROGRESS; dm_queue_if_no_path(mpp->alias, 0); } if (!flush_map(mpp, vecs, 1)) { condlog(2, "%s: removed map after" " removing all paths", alias); retval = 0; goto out; } /* * Not an error, continue */ } if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map for" " removal of path %s", mpp->alias, pp->dev); goto fail; } /* * reload the map */ mpp->action = ACT_RELOAD; if (domap(mpp, params) <= 0) { condlog(0, "%s: failed in domap for " "removal of path %s", mpp->alias, pp->dev); retval = 1; } else { /* * update our state from kernel */ if (setup_multipath(vecs, mpp)) return 1; sync_map_state(mpp); condlog(2, "%s [%s]: path removed from map %s", pp->dev, pp->dev_t, mpp->alias); } } out: if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return retval; fail: remove_map_and_stop_waiter(mpp, vecs, 1); return 1; } static int uev_update_path (struct uevent *uev, struct vectors * vecs) { int ro, retval = 0; ro = uevent_get_disk_ro(uev); if (ro >= 0) { struct path * pp; condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro); pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (!pp) { condlog(0, "%s: spurious uevent, path not found", uev->kernel); return 1; } if (pp->mpp) { retval = reload_map(vecs, pp->mpp, 0); condlog(2, "%s: map %s reloaded (retval %d)", uev->kernel, pp->mpp->alias, retval); } } return retval; } static int map_discovery (struct vectors * vecs) { struct multipath * mpp; unsigned int i; if (dm_get_maps(vecs->mpvec)) return 1; vector_foreach_slot (vecs->mpvec, mpp, i) if (setup_multipath(vecs, mpp)) return 1; return 0; } int uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data) { struct vectors * vecs; int r; *reply = NULL; *len = 0; vecs = (struct vectors *)trigger_data; pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(vecs->lock); pthread_testcancel(); r = parse_cmd(str, reply, len, vecs); if (r > 0) { *reply = STRDUP("fail\n"); *len = strlen(*reply) + 1; r = 1; } else if (!r && *len == 0) { *reply = STRDUP("ok\n"); *len = strlen(*reply) + 1; r = 0; } /* else if (r < 0) leave *reply alone */ lock_cleanup_pop(vecs->lock); return r; } static int uev_discard(char * devpath) { char *tmp; char a[11], b[11]; /* * keep only block devices, discard partitions */ tmp = strstr(devpath, "/block/"); if (tmp == NULL){ condlog(4, "no /block/ in '%s'", devpath); return 1; } if (sscanf(tmp, "/block/%10s", a) != 1 || sscanf(tmp, "/block/%10[^/]/%10s", a, b) == 2) { condlog(4, "discard event on %s", devpath); return 1; } return 0; } int uev_trigger (struct uevent * uev, void * trigger_data) { int r = 0; struct vectors * vecs; vecs = (struct vectors *)trigger_data; if (uev_discard(uev->devpath)) return 0; pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(vecs->lock); pthread_testcancel(); /* * device map event * Add events are ignored here as the tables * are not fully initialised then. */ if (!strncmp(uev->kernel, "dm-", 3)) { if (!strncmp(uev->action, "change", 6)) { r = uev_add_map(uev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_map(uev, vecs); goto out; } goto out; } /* * path add/remove event */ if (filter_devnode(conf->blist_devnode, conf->elist_devnode, uev->kernel) > 0) goto out; if (!strncmp(uev->action, "add", 3)) { r = uev_add_path(uev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_path(uev, vecs); goto out; } if (!strncmp(uev->action, "change", 6)) { r = uev_update_path(uev, vecs); goto out; } out: lock_cleanup_pop(vecs->lock); return r; } static void * ueventloop (void * ap) { struct udev *udev = ap; if (uevent_listen(udev)) condlog(0, "error starting uevent listener"); return NULL; } static void * uevqloop (void * ap) { if (uevent_dispatch(&uev_trigger, ap)) condlog(0, "error starting uevent dispatcher"); return NULL; } static void * uxlsnrloop (void * ap) { if (cli_init()) return NULL; set_handler_callback(LIST+PATHS, cli_list_paths); set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); set_handler_callback(LIST+PATH, cli_list_path); set_handler_callback(LIST+MAPS, cli_list_maps); set_handler_callback(LIST+STATUS, cli_list_status); set_handler_callback(LIST+DAEMON, cli_list_daemon); set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); set_handler_callback(LIST+CONFIG, cli_list_config); set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); set_handler_callback(LIST+DEVICES, cli_list_devices); set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); set_handler_callback(ADD+PATH, cli_add_path); set_handler_callback(DEL+PATH, cli_del_path); set_handler_callback(ADD+MAP, cli_add_map); set_handler_callback(DEL+MAP, cli_del_map); set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); set_handler_callback(RECONFIGURE, cli_reconfigure); set_handler_callback(SUSPEND+MAP, cli_suspend); set_handler_callback(RESUME+MAP, cli_resume); set_handler_callback(RESIZE+MAP, cli_resize); set_handler_callback(RELOAD+MAP, cli_reload); set_handler_callback(RESET+MAP, cli_reassign); set_handler_callback(REINSTATE+PATH, cli_reinstate); set_handler_callback(FAIL+PATH, cli_fail); set_handler_callback(DISABLEQ+MAP, cli_disable_queueing); set_handler_callback(RESTOREQ+MAP, cli_restore_queueing); set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing); set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing); set_handler_callback(QUIT, cli_quit); set_handler_callback(SHUTDOWN, cli_shutdown); set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus); set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus); set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus); set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q); set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q); umask(077); uxsock_listen(&uxsock_trigger, ap); return NULL; } void exit_daemon (void) { sem_post(&exit_sem); } const char * daemon_status(void) { switch (running_state) { case DAEMON_INIT: return "init"; case DAEMON_START: return "startup"; case DAEMON_CONFIGURE: return "configure"; case DAEMON_RUNNING: return "running"; case DAEMON_SHUTDOWN: return "shutdown"; } return NULL; } static void fail_path (struct path * pp, int del_active) { if (!pp->mpp) return; condlog(2, "checker failed path %s in map %s", pp->dev_t, pp->mpp->alias); dm_fail_path(pp->mpp->alias, pp->dev_t); if (del_active) update_queue_mode_del_path(pp->mpp); } /* * caller must have locked the path list before calling that function */ static int reinstate_path (struct path * pp, int add_active) { int ret = 0; if (!pp->mpp) return 0; if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) { condlog(0, "%s: reinstate failed", pp->dev_t); ret = 1; } else { condlog(2, "%s: reinstated", pp->dev_t); if (add_active) update_queue_mode_add_path(pp->mpp); } return ret; } static void enable_group(struct path * pp) { struct pathgroup * pgp; /* * if path is added through uev_add_path, pgindex can be unset. * next update_strings() will set it, upon map reload event. * * we can safely return here, because upon map reload, all * PG will be enabled. */ if (!pp->mpp->pg || !pp->pgindex) return; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); if (pgp->status == PGSTATE_DISABLED) { condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex); dm_enablegroup(pp->mpp->alias, pp->pgindex); } } static void mpvec_garbage_collector (struct vectors * vecs) { struct multipath * mpp; unsigned int i; if (!vecs->mpvec) return; 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); i--; } } } /* This is called after a path has started working again. It the multipath * device for this path uses the followover failback type, and this is the * best pathgroup, and this is the first path in the pathgroup to come back * up, then switch to this pathgroup */ static int followover_should_failback(struct path * pp) { struct pathgroup * pgp; struct path *pp1; int i; if (pp->mpp->pgfailback != -FAILBACK_FOLLOWOVER || !pp->mpp->pg || !pp->pgindex || pp->pgindex != pp->mpp->bestpg) return 0; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); vector_foreach_slot(pgp->paths, pp1, i) { if (pp1 == pp) continue; if (pp1->chkrstate != PATH_DOWN && pp1->chkrstate != PATH_SHAKY) return 0; } return 1; } static void defered_failback_tick (vector mpvec) { struct multipath * mpp; unsigned int i; vector_foreach_slot (mpvec, mpp, i) { /* * defered failback getting sooner */ if (mpp->pgfailback > 0 && mpp->failback_tick > 0) { mpp->failback_tick--; if (!mpp->failback_tick && need_switch_pathgroup(mpp, 1)) switch_pathgroup(mpp); } } } static void retry_count_tick(vector mpvec) { struct multipath *mpp; unsigned int i; vector_foreach_slot (mpvec, mpp, i) { if (mpp->retry_tick) { mpp->stat_total_queueing_time++; condlog(4, "%s: Retrying.. No active path", mpp->alias); if(--mpp->retry_tick == 0) { dm_queue_if_no_path(mpp->alias, 0); condlog(2, "%s: Disable queueing", mpp->alias); } } } } int update_prio(struct path *pp, int refresh_all) { int oldpriority; struct path *pp1; struct pathgroup * pgp; int i, j, changed = 0; if (refresh_all) { vector_foreach_slot (pp->mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp1, j) { oldpriority = pp1->priority; pathinfo(pp1, conf->hwtable, DI_PRIO); if (pp1->priority != oldpriority) changed = 1; } } return changed; } oldpriority = pp->priority; pathinfo(pp, conf->hwtable, DI_PRIO); if (pp->priority == oldpriority) return 0; return 1; } int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh) { if (reload_map(vecs, mpp, refresh)) return 1; dm_lib_release(); if (setup_multipath(vecs, mpp) != 0) return 1; sync_map_state(mpp); return 0; } /* * Returns '1' if the path has been checked, '0' otherwise */ int check_path (struct vectors * vecs, struct path * pp) { int newstate; int new_path_up = 0; int chkr_new_path_up = 0; int add_active; int oldchkrstate = pp->chkrstate; if (pp->initialized && !pp->mpp) return 0; if (pp->tick && --pp->tick) return 0; /* don't check this path yet */ /* * provision a next check soonest, * in case we exit abnormaly from here */ pp->tick = conf->checkint; newstate = path_offline(pp); /* * Wait for uevent for removed paths; * some LLDDs like zfcp keep paths unavailable * without sending uevents. */ if (newstate == PATH_REMOVED) newstate = PATH_DOWN; if (newstate == PATH_UP) newstate = get_state(pp, 1); else checker_clear_message(&pp->checker); if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { condlog(2, "%s: unusable path", pp->dev); pathinfo(pp, conf->hwtable, 0); return 1; } if (!pp->mpp) { if (!strlen(pp->wwid) && (newstate == PATH_UP || newstate == PATH_GHOST)) { condlog(2, "%s: add missing path", pp->dev); if (pathinfo(pp, conf->hwtable, DI_ALL) == 0) { ev_add_path(pp, vecs); pp->tick = 1; } } return 0; } /* * Async IO in flight. Keep the previous path state * and reschedule as soon as possible */ if (newstate == PATH_PENDING) { pp->tick = 1; return 0; } /* * Synchronize with kernel state */ if (update_multipath_strings(pp->mpp, vecs->pathvec)) { condlog(1, "%s: Could not synchronize with kernel state", pp->dev); pp->dmstate = PSTATE_UNDEF; } /* if update_multipath_strings orphaned the path, quit early */ if (!pp->mpp) return 0; if ((newstate == PATH_UP || newstate == PATH_GHOST) && pp->wait_checks > 0) { if (pp->mpp && pp->mpp->nr_active > 0) { pp->state = PATH_DELAYED; pp->wait_checks--; return 1; } else pp->wait_checks = 0; } pp->chkrstate = newstate; if (newstate != pp->state) { int oldstate = pp->state; pp->state = newstate; if (strlen(checker_message(&pp->checker))) LOG_MSG(1, checker_message(&pp->checker)); /* * upon state change, reset the checkint * to the shortest delay */ pp->checkint = conf->checkint; if (newstate == PATH_DOWN || newstate == PATH_SHAKY) { /* * proactively fail path in the DM */ if (oldstate == PATH_UP || oldstate == PATH_GHOST) { fail_path(pp, 1); if (pp->mpp->delay_wait_checks > 0 && pp->watch_checks > 0) { pp->wait_checks = pp->mpp->delay_wait_checks; pp->watch_checks = 0; } }else fail_path(pp, 0); /* * cancel scheduled failback */ pp->mpp->failback_tick = 0; pp->mpp->stat_path_failures++; return 1; } if(newstate == PATH_UP || newstate == PATH_GHOST){ if ( pp->mpp && pp->mpp->prflag ){ /* * Check Persistent Reservation. */ condlog(2, "%s: checking persistent reservation " "registration", pp->dev); mpath_pr_event_handle(pp); } } /* * reinstate this path */ if (oldstate != PATH_UP && oldstate != PATH_GHOST) { if (pp->mpp->delay_watch_checks > 0) pp->watch_checks = pp->mpp->delay_watch_checks; add_active = 1; } else { if (pp->watch_checks > 0) pp->watch_checks--; add_active = 0; } if (reinstate_path(pp, add_active)) { condlog(3, "%s: reload map", pp->dev); ev_add_path(pp, vecs); pp->tick = 1; return 0; } new_path_up = 1; if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST) chkr_new_path_up = 1; /* * if at least one path is up in a group, and * the group is disabled, re-enable it */ if (newstate == PATH_UP) enable_group(pp); } else if (newstate == PATH_UP || newstate == PATH_GHOST) { if (pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) { /* Clear IO errors */ if (reinstate_path(pp, 0)) { condlog(3, "%s: reload map", pp->dev); ev_add_path(pp, vecs); pp->tick = 1; return 0; } } else { LOG_MSG(4, checker_message(&pp->checker)); if (pp->checkint != conf->max_checkint) { /* * double the next check delay. * max at conf->max_checkint */ if (pp->checkint < (conf->max_checkint / 2)) pp->checkint = 2 * pp->checkint; else pp->checkint = conf->max_checkint; condlog(4, "%s: delay next check %is", pp->dev_t, pp->checkint); } if (pp->watch_checks > 0) pp->watch_checks--; pp->tick = pp->checkint; } } else if (newstate == PATH_DOWN && strlen(checker_message(&pp->checker))) { if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) LOG_MSG(3, checker_message(&pp->checker)); else LOG_MSG(2, checker_message(&pp->checker)); } pp->state = newstate; /* * path prio refreshing */ condlog(4, "path prio refresh"); if (update_prio(pp, new_path_up) && (pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) && pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) update_path_groups(pp->mpp, vecs, !new_path_up); else if (need_switch_pathgroup(pp->mpp, 0)) { if (pp->mpp->pgfailback > 0 && (new_path_up || pp->mpp->failback_tick <= 0)) pp->mpp->failback_tick = pp->mpp->pgfailback + 1; else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE || (chkr_new_path_up && followover_should_failback(pp))) switch_pathgroup(pp->mpp); } return 1; } static void * checkerloop (void *ap) { struct vectors *vecs; struct path *pp; int count = 0; unsigned int i; mlockall(MCL_CURRENT | MCL_FUTURE); vecs = (struct vectors *)ap; condlog(2, "path checkers start up"); /* * init the path check interval */ vector_foreach_slot (vecs->pathvec, pp, i) { pp->checkint = conf->checkint; } while (1) { struct timeval diff_time, start_time, end_time; int num_paths = 0; if (gettimeofday(&start_time, NULL) != 0) start_time.tv_sec = 0; pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(vecs->lock); pthread_testcancel(); condlog(4, "tick"); #ifdef USE_SYSTEMD if (use_watchdog) sd_notify(0, "WATCHDOG=1"); #endif if (vecs->pathvec) { vector_foreach_slot (vecs->pathvec, pp, i) { num_paths += check_path(vecs, pp); } } if (vecs->mpvec) { defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); } if (count) count--; else { condlog(4, "map garbage collection"); mpvec_garbage_collector(vecs); count = MAPGCINT; } lock_cleanup_pop(vecs->lock); if (start_time.tv_sec && gettimeofday(&end_time, NULL) == 0 && num_paths) { timersub(&end_time, &start_time, &diff_time); condlog(3, "checked %d path%s in %lu.%06lu secs", num_paths, num_paths > 1 ? "s" : "", diff_time.tv_sec, diff_time.tv_usec); } sleep(1); } return NULL; } int configure (struct vectors * vecs, int start_waiters) { struct multipath * mpp; struct path * pp; vector mpvec; int i, ret; if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) return 1; if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) return 1; if (!(mpvec = vector_alloc())) return 1; /* * probe for current path (from sysfs) and map (from dm) sets */ ret = path_discovery(vecs->pathvec, conf, DI_ALL); if (ret < 0) return 1; vector_foreach_slot (vecs->pathvec, pp, i){ if (filter_path(conf, pp) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; } else pp->checkint = conf->checkint; } if (map_discovery(vecs)) return 1; /* * create new set of maps & push changed ones into dm */ if (coalesce_paths(vecs, mpvec, NULL, 1)) return 1; /* * may need to remove some maps which are no longer relevant * e.g., due to blacklist changes in conf file */ if (coalesce_maps(vecs, mpvec)) return 1; dm_lib_release(); sync_maps_state(mpvec); vector_foreach_slot(mpvec, mpp, i){ remember_wwid(mpp->wwid); update_map_pr(mpp); } /* * purge dm of old maps */ remove_maps(vecs); /* * save new set of maps formed by considering current path state */ vector_free(vecs->mpvec); vecs->mpvec = mpvec; /* * start dm event waiter threads for these new maps */ vector_foreach_slot(vecs->mpvec, mpp, i) { if (setup_multipath(vecs, mpp)) return 1; if (start_waiters) if (start_waiter_thread(mpp, vecs)) return 1; } return 0; } int reconfigure (struct vectors * vecs) { struct config * old = conf; int retval = 1; running_state = DAEMON_CONFIGURE; /* * free old map and path vectors ... they use old conf state */ if (VECTOR_SIZE(vecs->mpvec)) remove_maps_and_stop_waiters(vecs); if (VECTOR_SIZE(vecs->pathvec)) free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; conf = NULL; /* Re-read any timezone changes */ tzset(); if (!load_config(DEFAULT_CONFIGFILE, udev)) { dm_drv_version(conf->version, TGT_MPATH); conf->verbosity = old->verbosity; conf->daemon = 1; configure(vecs, 1); free_config(old); retval = 0; } running_state = DAEMON_RUNNING; return retval; } static struct vectors * init_vecs (void) { struct vectors * vecs; vecs = (struct vectors *)MALLOC(sizeof(struct vectors)); if (!vecs) return NULL; vecs->lock.mutex = (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t)); if (!vecs->lock.mutex) goto out; pthread_mutex_init(vecs->lock.mutex, NULL); vecs->lock.depth = 0; return vecs; out: FREE(vecs); condlog(0, "failed to init paths"); return NULL; } static void * signal_set(int signo, void (*func) (int)) { int r; struct sigaction sig; struct sigaction osig; sig.sa_handler = func; sigemptyset(&sig.sa_mask); sig.sa_flags = 0; r = sigaction(signo, &sig, &osig); if (r < 0) return (SIG_ERR); else return (osig.sa_handler); } void handle_signals(void) { if (reconfig_sig && running_state == DAEMON_RUNNING) { condlog(2, "reconfigure (signal)"); pthread_cleanup_push(cleanup_lock, &gvecs->lock); lock(gvecs->lock); pthread_testcancel(); reconfigure(gvecs); lock_cleanup_pop(gvecs->lock); } if (log_reset_sig) { condlog(2, "reset log (signal)"); pthread_mutex_lock(&logq_lock); log_reset("multipathd"); pthread_mutex_unlock(&logq_lock); } reconfig_sig = 0; log_reset_sig = 0; } static void sighup (int sig) { reconfig_sig = 1; } static void sigend (int sig) { exit_daemon(); } static void sigusr1 (int sig) { log_reset_sig = 1; } static void sigusr2 (int sig) { condlog(3, "SIGUSR2 received"); } static void signal_init(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGHUP); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); pthread_sigmask(SIG_BLOCK, &set, NULL); signal_set(SIGHUP, sighup); signal_set(SIGUSR1, sigusr1); signal_set(SIGUSR2, sigusr2); signal_set(SIGINT, sigend); signal_set(SIGTERM, sigend); signal(SIGPIPE, SIG_IGN); } static void setscheduler (void) { int res; static struct sched_param sched_param = { .sched_priority = 99 }; res = sched_setscheduler (0, SCHED_RR, &sched_param); if (res == -1) condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); return; } static void set_oom_adj (void) { #ifdef OOM_SCORE_ADJ_MIN int retry = 1; char *file = "/proc/self/oom_score_adj"; int score = OOM_SCORE_ADJ_MIN; #else int retry = 0; char *file = "/proc/self/oom_adj"; int score = OOM_ADJUST_MIN; #endif FILE *fp; struct stat st; char *envp; envp = getenv("OOMScoreAdjust"); if (envp) { condlog(3, "Using systemd provided OOMScoreAdjust"); return; } do { if (stat(file, &st) == 0){ fp = fopen(file, "w"); if (!fp) { condlog(0, "couldn't fopen %s : %s", file, strerror(errno)); return; } fprintf(fp, "%i", score); fclose(fp); return; } if (errno != ENOENT) { condlog(0, "couldn't stat %s : %s", file, strerror(errno)); return; } #ifdef OOM_ADJUST_MIN file = "/proc/self/oom_adj"; score = OOM_ADJUST_MIN; #else retry = 0; #endif } while (retry--); condlog(0, "couldn't adjust oom score"); } static int child (void * param) { pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr; pthread_attr_t log_attr, misc_attr, uevent_attr; struct vectors * vecs; struct multipath * mpp; int i; #ifdef USE_SYSTEMD unsigned long checkint; #endif int rc, pid_rc; char *envp; mlockall(MCL_CURRENT | MCL_FUTURE); sem_init(&exit_sem, 0, 0); signal_init(); udev = udev_new(); setup_thread_attr(&misc_attr, 64 * 1024, 1); setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 1); setup_thread_attr(&waiter_attr, 32 * 1024, 1); if (logsink == 1) { setup_thread_attr(&log_attr, 64 * 1024, 0); log_thread_start(&log_attr); pthread_attr_destroy(&log_attr); } running_state = DAEMON_START; #ifdef USE_SYSTEMD sd_notify(0, "STATUS=startup"); #endif condlog(2, "--------start up--------"); condlog(2, "read " DEFAULT_CONFIGFILE); if (load_config(DEFAULT_CONFIGFILE, udev)) goto failed; dm_drv_version(conf->version, TGT_MPATH); if (init_checkers()) { condlog(0, "failed to initialize checkers"); goto failed; } if (init_prio()) { condlog(0, "failed to initialize prioritizers"); goto failed; } setlogmask(LOG_UPTO(conf->verbosity + 3)); envp = getenv("LimitNOFILE"); if (envp) { condlog(2,"Using systemd provided open fds limit of %s", envp); } else if (conf->max_fds) { struct rlimit fd_limit; if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { condlog(0, "can't get open fds limit: %s", strerror(errno)); fd_limit.rlim_cur = 0; fd_limit.rlim_max = 0; } if (fd_limit.rlim_cur < conf->max_fds) { fd_limit.rlim_cur = conf->max_fds; if (fd_limit.rlim_max < conf->max_fds) fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { condlog(0, "can't set open fds limit to " "%lu/%lu : %s", fd_limit.rlim_cur, fd_limit.rlim_max, strerror(errno)); } else { condlog(3, "set open fds limit to %lu/%lu", fd_limit.rlim_cur, fd_limit.rlim_max); } } } vecs = gvecs = init_vecs(); if (!vecs) goto failed; setscheduler(); set_oom_adj(); conf->daemon = 1; dm_udev_set_sync_support(0); #ifdef USE_SYSTEMD envp = getenv("WATCHDOG_USEC"); if (envp && sscanf(envp, "%lu", &checkint) == 1) { /* Value is in microseconds */ conf->max_checkint = checkint / 1000000; /* Rescale checkint */ if (conf->checkint > conf->max_checkint) conf->checkint = conf->max_checkint; else conf->checkint = conf->max_checkint / 4; condlog(3, "enabling watchdog, interval %d max %d", conf->checkint, conf->max_checkint); use_watchdog = conf->checkint; } #endif /* * Start uevent listener early to catch events */ if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) { condlog(0, "failed to create uevent thread: %d", rc); goto failed; } pthread_attr_destroy(&uevent_attr); if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) { condlog(0, "failed to create cli listener: %d", rc); goto failed; } /* * fetch and configure both paths and multipaths */ #ifdef USE_SYSTEMD sd_notify(0, "STATUS=configure"); #endif running_state = DAEMON_CONFIGURE; lock(vecs->lock); if (configure(vecs, 1)) { unlock(vecs->lock); condlog(0, "failure during configuration"); goto failed; } unlock(vecs->lock); /* * start threads */ if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) { condlog(0,"failed to create checker loop thread: %d", rc); goto failed; } if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) { condlog(0, "failed to create uevent dispatcher: %d", rc); goto failed; } pthread_attr_destroy(&misc_attr); /* Startup complete, create logfile */ pid_rc = pidfile_create(DEFAULT_PIDFILE, daemon_pid); /* Ignore errors, we can live without */ running_state = DAEMON_RUNNING; #ifdef USE_SYSTEMD sd_notify(0, "READY=1\nSTATUS=running"); #endif /* * exit path */ while(sem_wait(&exit_sem) != 0); /* Do nothing */ #ifdef USE_SYSTEMD sd_notify(0, "STATUS=shutdown"); #endif running_state = DAEMON_SHUTDOWN; lock(vecs->lock); if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) vector_foreach_slot(vecs->mpvec, mpp, i) dm_queue_if_no_path(mpp->alias, 0); remove_maps_and_stop_waiters(vecs); unlock(vecs->lock); pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); pthread_cancel(uevq_thr); lock(vecs->lock); free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; unlock(vecs->lock); /* Now all the waitevent threads will start rushing in. */ while (vecs->lock.depth > 0) { sleep (1); /* This is weak. */ condlog(3, "Have %d wait event checkers threads to de-alloc," " waiting...", vecs->lock.depth); } pthread_mutex_destroy(vecs->lock.mutex); FREE(vecs->lock.mutex); vecs->lock.depth = 0; vecs->lock.mutex = NULL; FREE(vecs); vecs = NULL; cleanup_checkers(); cleanup_prio(); dm_lib_release(); dm_lib_exit(); /* We're done here */ if (!pid_rc) { condlog(3, "unlink pidfile"); unlink(DEFAULT_PIDFILE); } condlog(2, "--------shut down-------"); if (logsink == 1) log_thread_stop(); /* * Freeing config must be done after condlog() and dm_lib_exit(), * because logging functions like dlog() and dm_write_log() * reference the config. */ free_config(conf); conf = NULL; udev_unref(udev); udev = NULL; #ifdef _DEBUG_ dbg_free_final(NULL); #endif #ifdef USE_SYSTEMD sd_notify(0, "ERRNO=0"); #endif exit(0); failed: #ifdef USE_SYSTEMD sd_notify(0, "ERRNO=1"); #endif exit(1); } static int daemonize(void) { int pid; int dev_null_fd; if( (pid = fork()) < 0){ fprintf(stderr, "Failed first fork : %s\n", strerror(errno)); return -1; } else if (pid != 0) return pid; setsid(); if ( (pid = fork()) < 0) fprintf(stderr, "Failed second fork : %s\n", strerror(errno)); else if (pid != 0) _exit(0); if (chdir("/") < 0) fprintf(stderr, "cannot chdir to '/', continuing\n"); dev_null_fd = open("/dev/null", O_RDWR); if (dev_null_fd < 0){ fprintf(stderr, "cannot open /dev/null for input & output : %s\n", strerror(errno)); _exit(0); } close(STDIN_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stdin : %s\n", strerror(errno)); _exit(0); } close(STDOUT_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stdout : %s\n", strerror(errno)); _exit(0); } close(STDERR_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stderr : %s\n", strerror(errno)); _exit(0); } close(dev_null_fd); daemon_pid = getpid(); return 0; } int main (int argc, char *argv[]) { extern char *optarg; extern int optind; int arg; int err; int foreground = 0; logsink = 1; running_state = DAEMON_INIT; dm_init(); if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } /* make sure we don't lock any path */ if (chdir("/") < 0) fprintf(stderr, "can't chdir to root directory : %s\n", strerror(errno)); umask(umask(077) | 022); conf = alloc_config(); if (!conf) exit(1); while ((arg = getopt(argc, argv, ":dsv:k::B")) != EOF ) { switch(arg) { case 'd': foreground = 1; if (logsink > 0) logsink = 0; //debug=1; /* ### comment me out ### */ break; case 'v': if (sizeof(optarg) > sizeof(char *) || !isdigit(optarg[0])) exit(1); conf->verbosity = atoi(optarg); break; case 's': logsink = -1; break; case 'k': if (load_config(DEFAULT_CONFIGFILE, udev_new())) exit(1); uxclnt(optarg, conf->uxsock_timeout); exit(0); case 'B': conf->bindings_read_only = 1; break; default: ; } } if (optind < argc) { char cmd[CMDSIZE]; char * s = cmd; char * c = s; if (load_config(DEFAULT_CONFIGFILE, udev_new())) exit(1); while (optind < argc) { if (strchr(argv[optind], ' ')) c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]); else c += snprintf(c, s + CMDSIZE - c, "%s ", argv[optind]); optind++; } c += snprintf(c, s + CMDSIZE - c, "\n"); uxclnt(s, conf->uxsock_timeout); exit(0); } if (foreground) { if (!isatty(fileno(stdout))) setbuf(stdout, NULL); err = 0; daemon_pid = getpid(); } else err = daemonize(); if (err < 0) /* error */ exit(1); else if (err > 0) /* parent dies */ exit(0); else /* child lives */ return (child(NULL)); } void * mpath_pr_event_handler_fn (void * pathp ) { struct multipath * mpp; int i,j, ret, isFound; struct path * pp = (struct path *)pathp; unsigned char *keyp; uint64_t prkey; struct prout_param_descriptor *param; struct prin_resp *resp; 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; } ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s : pr in read keys service action failed. Error=%d", pp->dev, ret); goto out; } condlog(3, " event pr=%d addlen=%d",resp->prin_descriptor.prin_readkeys.prgeneration, resp->prin_descriptor.prin_readkeys.additional_length ); if (resp->prin_descriptor.prin_readkeys.additional_length == 0 ) { condlog(1, "%s: No key found. Device may not be registered.", pp->dev); ret = MPATH_PR_SUCCESS; goto out; } prkey = 0; keyp = (unsigned char *)mpp->reservation_key; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= *keyp; ++keyp; } condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ", prkey); isFound =0; for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ ) { condlog(2, "PR IN READKEYS[%d] reservation key:",i); dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1); if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8)) { condlog(2, "%s: pr key found in prin readkeys response", mpp->alias); isFound =1; break; } } if (!isFound) { condlog(0, "%s: Either device not registered or ", pp->dev); condlog(0, "host is not authorised for registration. Skip path"); ret = MPATH_PR_OTHER; goto out; } param= malloc(sizeof(struct prout_param_descriptor)); memset(param, 0 , sizeof(struct prout_param_descriptor)); for (j = 7; j >= 0; --j) { param->sa_key[j] = (prkey & 0xff); prkey >>= 8; } param->num_transportid = 0; condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid); ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, param, 0); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret); } mpp->prflag = 1; free(param); out: free(resp); return NULL; } int mpath_pr_event_handle(struct path *pp) { pthread_t thread; int rc; pthread_attr_t attr; struct multipath * mpp; mpp = pp->mpp; if (!mpp->reservation_key) return -1; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); rc = pthread_create(&thread, NULL , mpath_pr_event_handler_fn, pp); if (rc) { condlog(0, "%s: ERROR; return code from pthread_create() is %d", pp->dev, rc); return -1; } pthread_attr_destroy(&attr); rc = pthread_join(thread, NULL); return 0; } multipath-tools-0.5.0+git1.656f8865/multipathd/main.h000066400000000000000000000022201260771327300220150ustar00rootroot00000000000000#ifndef MAIN_H #define MAIN_H #define MAPGCINT 5 enum daemon_status { DAEMON_INIT, DAEMON_START, DAEMON_CONFIGURE, DAEMON_RUNNING, DAEMON_SHUTDOWN, }; struct prout_param_descriptor; struct prin_resp; extern pid_t daemon_pid; void exit_daemon(void); const char * daemon_status(void); int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *); int ev_remove_path (struct path *, struct vectors *); int ev_add_map (char *, char *, struct vectors *); int ev_remove_map (char *, char *, int, struct vectors *); void sync_map_state (struct multipath *); void * mpath_alloc_prin_response(int prin_sa); int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, int noisy); void dumpHex(const char * , int len, int no_ascii); int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *param, int noisy); int mpath_pr_event_handle(struct path *pp); 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(void); #endif /* MAIN_H */ multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.8000066400000000000000000000155741260771327300232040ustar00rootroot00000000000000.TH MULTIPATHD 8 "November 2009" "Linux Administrator's Manual" .SH NAME multipathd \- multipath daemon .SH SYNOPSIS .B multipathd .RB [\| options \|] .SH DESCRIPTION The .B multipathd daemon is in charge of checking for failed paths. When this happens, it will reconfigure the multipath map the path belongs to, so that this map regains its maximum performance and redundancy. This daemon executes the external multipath config tool when events occur. In turn, the multipath tool signals the multipathd daemon when it is done with devmap reconfiguration, so that it can refresh its failed path list. .SH OPTIONS .TP .B \-d Foreground Mode. Don't daemonize, and print all messages to stdout and stderr. .TP .B \-s Suppress timestamps. Do not prefix logging messages with a timestamp. .TP .B -v "level" Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. .TP .B -B Read-only bindings file. Multipathd will not write to the user_friendly_names bindings file. If a user_friendly_name doesn't already exist for a device, it will use its WWID as its alias. .TP .B -k multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering "help". When you are finished entering commands, press CTRL-D to quit. .SH COMMANDS .TP The following commands can be used in interactive mode: .TP .B list|show paths Show the paths that multipathd is monitoring, and their state. .TP .B list|show paths format $format Show the paths that multipathd is monitoring, using a format string with path format wildcards. .TP .B list|show maps|multipaths Show the multipath devices that the multipathd is monitoring. .TP .B list|show maps|multipaths format $format Show the status of all multipath devices that the multipathd is monitoring, using a format string with multipath format wildcards. .TP .B list|show maps|multipaths status Show the status of all multipath devices that the multipathd is monitoring. .TP .B list|show maps|multipaths stats Show some statistics of all multipath devices that the multipathd is monitoring. .TP .B list|show maps|multipaths topology Show the current multipath topology. Same as "multipath \-ll". .TP .B list|show topology Show the current multipath topology. Same as "multipath \-ll". .TP .B list|show map|multipath $map topology Show topology of a single multipath device specified by $map, e.g. 36005076303ffc56200000000000010aa. This map could be obtained from "list maps". .TP .B list|show wildcards Show the format wildcards used in interactive commands taking $format .TP .B list|show config Show the currently used configuration, derived from default values and values specified within the configuration file /etc/multipath.conf. .TP .B list|show blacklist Show the currently used blacklist rules, derived from default values and values specified within the configuration file /etc/multipath.conf. .TP .B list|show devices Show all available block devices by name including the information if they are blacklisted or not. .TP .B list|show status Show the number of path checkers in each possible state, the number of monitored paths, and whether multipathd is currently handling a uevent. .TP .B list|show daemon Show the current state of the multipathd daemon .TP .B add path $path Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). .TP .B remove|del path $path Stop monitoring a path. $path is as listed in /sys/block (e.g. sda). .TP .B add map|multipath $map Add a multipath device to the list of monitored devices. $map can either be a device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias for the multipath device (e.g. mpath1) or the uid of the multipath device (e.g. 36005076303ffc56200000000000010aa). .TP .B remove|del map|multipath $map Stop monitoring a multipath device. .TP .B resize map|multipath $map Resizes map $map to the given size .TP .B switch|switchgroup map|multipath $map group $group Force a multipath device to switch to a specific path group. $group is the path group index, starting with 1. .TP .B reconfigure Reconfigures the multipaths. This should be triggered automatically after any hotplug event. .TP .B suspend map|multipath $map Sets map $map into suspend state. .TP .B resume map|multipath $map Resumes map $map from suspend state. .TP .B reset map|multipath $map Reassign existing device-mapper table(s) use use the multipath device, instead of its path devices. .TP .B reload map|multipath $map Reload a multipath device. .TP .B fail path $path Sets path $path into failed state. .TP .B reinstate path $path Resumes path $path from failed state. .TP .B disablequeueing maps|multipaths Disable queueing on all multipath devices. .TP .B restorequeueing maps|multipaths Restore queueing on all multipath devices. .TP .B disablequeueing map|multipath $map Disable queuing on multipathed map $map .TP .B restorequeueing map|multipath $map Restore queuing on multipahted map $map .TP .B forcequeueing daemon Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing will not be disabled when the daemon stops .TP .B restorequeueing daemon Restores configured queue_without_daemon mode .TP .B map|multipath $map setprstatus Enable persistent reservation management on $map .TP .B map|multipath $map unsetprstatus Disable persistent reservation management on $map .TP .B map|multipath $map getprstatus Get the current persistent reservation management status of $map .TP .B quit|exit End interactive session. .TP .B shutdown Stop multipathd. .SH "SYSTEMD INTEGRATION" When compiled with systemd support two systemd service files are installed, .I multipathd.service and .I multipathd.socket The .I multipathd.socket service instructs systemd to intercept the CLI command socket, so that any call to the CLI interface will start-up the daemon if required. The .I multipathd.service file carries the definitions for controlling the multipath daemon. The daemon itself uses the .B sd_notify(3) interface to communicate with systemd. The following unit keywords are recognized: .TP .I WatchdogSec= Enables the internal watchdog from systemd. multipath will send a notification via .B sd_notify(3) to systemd to reset the watchdog. If specified the .I polling_interval and .I max_polling_interval settings will be overridden by the watchdog settings. Please note that systemd prior to version 207 has issues which prevent the systemd-provided watchdog from working correctly. So the watchdog is not enabled per default, but has to be enabled manually by updating the multipathd.service file. .TP .I OOMScoreAdjust= Overrides the internal OOM adjust mechanism .TP .I LimitNOFILE= Overrides the .I max_fds configuration setting. .SH "SEE ALSO" .BR multipath (8) .BR kpartx (8) .BR sd_notify (3) .BR system.service (5) .SH "AUTHORS" .B multipathd was developed by Christophe Varoqui, and others. multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.init.debian000066400000000000000000000006611260771327300252100ustar00rootroot00000000000000#!/bin/sh PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/usr/bin/multipathd test -x $DAEMON || exit 0 case "$1" in start) echo -n "Starting multipath daemon: multipathd" $DAEMON echo "." ;; stop) echo -n "Stopping multipath daemon: multipathd" echo "." $DAEMON shutdown ;; force-reload|restart) $0 stop $0 start ;; *) echo "Usage: /etc/init.d/multipathd {start|stop|restart|force-reload}" exit 1 ;; esac exit 0 multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.init.redhat000066400000000000000000000051541260771327300252370ustar00rootroot00000000000000#!/bin/bash # # multipathd Starts the multipath daemon # # chkconfig: - 06 87 # description: Manages device-mapper multipath devices ### BEGIN INIT INFO # Provides: multipathd # Required-Start: # Required-Stop: # Default-Start: # Default-Stop: # Short-Description: Control multipathd # Description: This service monitors and manages # device-mapper multipath devices ### END INIT INFO DAEMON=/sbin/multipathd prog=`basename $DAEMON` initdir=/etc/rc.d/init.d lockdir=/var/lock/subsys sysconfig=/etc/sysconfig syspath=/sys/block . $initdir/functions test -r $sysconfig/$prog && . $sysconfig/$prog RETVAL=0 teardown_slaves() { pushd $1 > /dev/null if [ -d "slaves" ]; then for slave in slaves/*; do if [ "$slave" = "slaves/*" ]; then read dev < $1/dev tablename=`dmsetup table --target multipath | sed -n "s/\(.*\): .* $dev .*/\1/p"` if ! [ -z $tablename ]; then echo "Root is on a multipathed device, multipathd can not be stopped" exit 1 fi else local_slave=`readlink -f $slave`; teardown_slaves $local_slave; fi done else read dev < $1/dev tablename=`dmsetup table --target multipath | sed -n "s/\(.*\): .* $dev .*/\1/p"` if ! [ -z $tablename ]; then echo "Root is on a multipathed device, multipathd can not be stopped" exit 1 fi fi popd > /dev/null } # # See how we were called. # start() { test -x $DAEMON || exit 5 echo -n $"Starting $prog daemon: " daemon $DAEMON RETVAL=$? [ $RETVAL -eq 0 ] && touch $lockdir/$prog echo } force_stop() { echo -n $"Stopping $prog daemon: " killproc $DAEMON RETVAL=$? [ $RETVAL -eq 0 ] && rm -f $lockdir/$prog echo } check_root() { root_dev=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $1; }}' /etc/mtab) dm_num=`dmsetup info -c --noheadings -o minor $root_dev 2> /dev/null` if [ $? -eq 0 ]; then root_dm_device="dm-$dm_num" [ -d $syspath/$root_dm_device ] && teardown_slaves $syspath/$root_dm_device fi } force_queue_without_daemon() { $DAEMON forcequeueing daemon } restart() { force_queue_without_daemon check_root force_stop start } force_restart() { force_queue_without_daemon force_stop start } reload() { echo -n "Reloading $prog: " trap "" SIGHUP killproc $DAEMON -HUP RETVAL=$? echo } case "$1" in start) start ;; stop) check_root force_stop ;; force-stop) force_stop ;; force-reload|reload) reload ;; restart) restart ;; force-restart) force_restart ;; condrestart|try-restart) if [ -f $lockdir/$prog ]; then restart fi ;; status) status $prog RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|force-stop|status|restart|force-restart|condrestart|reload}" RETVAL=2 esac exit $RETVAL multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.init.suse000066400000000000000000000107201260771327300247420ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. # # Author: Hannes Reinecke # # init.d/multipathd # ### BEGIN INIT INFO # Provides: multipathd # Required-Start: $syslog # Required-Stop: $syslog # Default-Start: 3 5 # Default-Stop: 0 1 2 4 6 # Short-Description: Starts multipath daemon # Description: Starts the multipath daemon ### END INIT INFO PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/sbin/multipathd PIDFILE=/var/run/multipathd.pid MPATH_INIT_TIMEOUT=10 ARGS="" # Set the maximum number of open files MAX_OPEN_FDS=4096 # Set to enable asynchronous daemon startup DAEMON_ASYNC_STARTUP= test -x $DAEMON || exit 5 . /etc/rc.status # First reset status of this service rc_reset case "$1" in start) echo -n "Starting multipathd" ulimit -c unlimited if $DAEMON -k"reconfigure" > /dev/null 2>&1 ; then echo -n " (multipathd running)" rc_status -v rc_exit fi modprobe dm-multipath # Set the maximum number of open files if [ -n "$MAX_OPEN_FDS" ] ; then ulimit -n $MAX_OPEN_FDS fi $DAEMON $ARGS if [ -n "$DAEMON_ASYNC_STARTUP" ] ; then rc_status -v rc_exit fi # Wait for the daemon to start up status=$($DAEMON -k'show daemon' 2> /dev/null) timeout=$MPATH_INIT_TIMEOUT while [ $timeout -gt 0 ] ; do if [ -n "$status" ] ; then PID=${status##pid } STATUS=${PID##* } # Configuration might be taking some time, # so don't increase the timeout here [ "$STATUS" != "configure" ] && timeout=$(( $timeout - 1 )) [ "$STATUS" == "running" ] && break else timeout=$(( $timeout - 1 )) fi sleep 1 status=$($DAEMON -k'show daemon' 2> /dev/null) done if [ -z "$status" ] ; then rc_failed 7 fi if [ $timeout -le 0 ] ; then rc_failed 1 fi # Remember status and be verbose rc_status -v ;; stop) echo -n "Shutting down multipathd" STATUS="unknown" # Try to get PID from daemon status=$($DAEMON -k'show daemon' 2> /dev/null) if [ -n "$status" ] ; then PID=${status##pid } STATUS=${PID##* } PID=${PID%% *} fi # Fallback to PID file for older versions if [ -z "$PID" ] || [ "$PID" == "multipath-tools" ] ; then if [ -f $PIDFILE ]; then PID="$(cat $PIDFILE)" STATUS="running" else rc_failed 7 rc_status -v rc_exit fi fi # Shutdown the daemon via CLI if [ "$STATUS" = "running" ] ; then status=$($DAEMON -k'shutdown' 2> /dev/null) if [ "$status" = "ok" ] ; then timeout=$MPATH_INIT_TIMEOUT while [ $timeout -gt 0 ] ; do PROCNAME="$(ps -p $PID -o comm=)" if [ "$PROCNAME" != "multipathd" ] && [ "$PROCNAME" != "multipathd " ] ; then STATUS="shutdown" break fi sleep 1 timeout=$(( $timeout - 1 )) done fi fi # Kill the daemon if the above failed if [ -n "$PID" -a "$STATUS" != "shutdown" ] ; then kill -TERM $PID timeout=$MPATH_INIT_TIMEOUT while [ $timeout -gt 0 ] ; do PROCNAME="$(ps -p $PID -o comm=)" if [ "$PROCNAME" != "multipathd" ] && [ "$PROCNAME" != "multipathd " ] ; then STATUS="shutdown" break fi sleep 1 timeout=$(( $timeout - 1 )) done fi if [ $STATUS != "shutdown" ] ; then echo -n " (still running)" rc_failed 1 fi # Remember status and be verbose rc_status -v ;; try-restart) ## Stop the service and if this succeeds (i.e. the ## service was running before), start it again. $0 status >/dev/null && $0 restart # Remember status and be quiet rc_status ;; restart|force-reload) ## Stop the service and regardless of whether it was ## running or not, start it again. $0 stop $0 start # Remember status and be quiet rc_status ;; reload) ## Like force-reload, but if daemon does not support ## signalling, do nothing (!) $DAEMON -k"reconfigure" > /dev/null 2>&1 # Remember status and be quiet rc_status ;; status) echo -n "Checking for multipathd: " # Status has a slightly different for the status command: # 0 - service running # 1 - service dead, but /var/run/ pid file exists # 2 - service dead, but /var/lock/ lock file exists # 3 - service not running status=$($DAEMON -k'show daemon' 2> /dev/null) if [ -n "$status" ]; then (exit 0) else (exit 3) fi rc_status -v ;; probe) ## Optional: Probe for the necessity of a reload, ## give out the argument which is required for a reload. ;; *) echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" exit 1 ;; esac rc_exit multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.service000066400000000000000000000010311260771327300244540ustar00rootroot00000000000000[Unit] Description=Device-Mapper Multipath Device Controller Requires=blk-availability.service Before=iscsi.service iscsid.service lvm2-activation-early.service Before=local-fs-pre.target After=multipathd.socket DefaultDependencies=no Wants=local-fs-pre.target multipathd.socket Conflicts=shutdown.target [Service] Type=notify NotifyAccess=main LimitCORE=infinity ExecStartPre=/sbin/modprobe dm-multipath ExecStart=/sbin/multipathd -d -s ExecReload=/sbin/multipathd reconfigure [Install] WantedBy=sysinit.target Also=multipathd.socket multipath-tools-0.5.0+git1.656f8865/multipathd/multipathd.socket000066400000000000000000000002271260771327300243120ustar00rootroot00000000000000[Unit] Description=multipathd control socket DefaultDependencies=no Before=sockets.target [Socket] ListenStream=@/org/kernel/linux/storage/multipathd multipath-tools-0.5.0+git1.656f8865/multipathd/pidfile.c000066400000000000000000000034241260771327300225070ustar00rootroot00000000000000#include /* for pid_t */ #include /* for open */ #include /* for kill() */ #include /* for ESHRC */ #include /* for f...() */ #include /* for memset() */ #include /* for atoi() */ #include /* for unlink() */ #include /* for fcntl() */ #include #include "pidfile.h" int pidfile_create(const char *pidFile, pid_t pid) { char buf[20]; struct flock lock; int fd, value; if((fd = open(pidFile, O_WRONLY | O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) { condlog(0, "Cannot open pidfile [%s], error was [%s]", pidFile, strerror(errno)); return 1; } lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if (errno != EACCES && errno != EAGAIN) condlog(0, "Cannot lock pidfile [%s], error was [%s]", pidFile, strerror(errno)); else condlog(0, "process is already running"); goto fail; } if (ftruncate(fd, 0) < 0) { condlog(0, "Cannot truncate pidfile [%s], error was [%s]", pidFile, strerror(errno)); goto fail; } memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf)-1, "%u", pid); if (write(fd, buf, strlen(buf)) != strlen(buf)) { condlog(0, "Cannot write pid to pidfile [%s], error was [%s]", pidFile, strerror(errno)); goto fail; } if ((value = fcntl(fd, F_GETFD, 0)) < 0) { condlog(0, "Cannot get close-on-exec flag from pidfile [%s], " "error was [%s]", pidFile, strerror(errno)); goto fail; } value |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, value) < 0) { condlog(0, "Cannot set close-on-exec flag from pidfile [%s], " "error was [%s]", pidFile, strerror(errno)); goto fail; } return 0; fail: close(fd); return 1; } multipath-tools-0.5.0+git1.656f8865/multipathd/pidfile.h000066400000000000000000000000641260771327300225110ustar00rootroot00000000000000int pidfile_create(const char *pidFile, pid_t pid); multipath-tools-0.5.0+git1.656f8865/multipathd/uxclnt.c000066400000000000000000000047101260771327300224070ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cli.h" static void print_reply(char *s) { if (isatty(1)) { printf("%s", s); return; } /* strip ANSI color markers */ while (*s != '\0') { if ((*s == 0x1b) && (*(s+1) == '[')) while ((*s++ != 'm') && (*s != '\0')) {}; putchar(*s++); } } static int need_quit(char *str, size_t len) { char *ptr, *start; size_t trimed_len = len; for (ptr = str; trimed_len && isspace(*ptr); trimed_len--, ptr++) ; start = ptr; for (ptr = str + len - 1; trimed_len && isspace(*ptr); trimed_len--, ptr--) ; if ((trimed_len == 4 && !strncmp(start, "exit", 4)) || (trimed_len == 4 && !strncmp(start, "quit", 4))) return 1; return 0; } /* * process the client */ static void process(int fd, unsigned int timeout) { char *line; char *reply; int ret; cli_init(); rl_readline_name = "multipathd"; rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { size_t len; size_t llen = strlen(line); if (!llen) { free(line); continue; } if (need_quit(line, llen)) break; if (send_packet(fd, line, llen + 1) != 0) break; ret = recv_packet(fd, &reply, &len, timeout); if (ret != 0) break; print_reply(reply); if (line && *line) add_history(line); free(line); FREE(reply); } } static void process_req(int fd, char * inbuf, unsigned int timeout) { char *reply; size_t len; int ret; if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) { printf("cannot send packet\n"); return; } ret = recv_packet(fd, &reply, &len, timeout); if (ret < 0) { if (ret == -ETIMEDOUT) printf("timeout receiving packet\n"); else printf("error %d receiving packet\n", ret); } else { printf("%s", reply); FREE(reply); } } /* * entry point */ int uxclnt(char * inbuf, unsigned int timeout) { int fd; fd = ux_socket_connect(DEFAULT_SOCKET); if (fd == -1) exit(1); if (inbuf) process_req(fd, inbuf, timeout); else process(fd, timeout); return 0; } multipath-tools-0.5.0+git1.656f8865/multipathd/uxclnt.h000066400000000000000000000000601260771327300224060ustar00rootroot00000000000000int uxclnt(char * inbuf, unsigned int timeout); multipath-tools-0.5.0+git1.656f8865/multipathd/uxlsnr.c000066400000000000000000000125731260771327300224330ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ /* * A simple domain socket listener */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "cli.h" #include "uxlsnr.h" struct timespec sleep_time = {5, 0}; struct client { struct list_head node; int fd; }; LIST_HEAD(clients); pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; struct pollfd *polls; volatile sig_atomic_t reconfig_sig = 0; volatile sig_atomic_t log_reset_sig = 0; /* * handle a new client joining */ static void new_client(int ux_sock) { struct client *c; struct sockaddr addr; socklen_t len = sizeof(addr); int fd; fd = accept(ux_sock, &addr, &len); if (fd == -1) return; c = (struct client *)MALLOC(sizeof(*c)); if (!c) { close(fd); return; } memset(c, 0, sizeof(*c)); INIT_LIST_HEAD(&c->node); c->fd = fd; /* put it in our linked list */ pthread_mutex_lock(&client_lock); list_add_tail(&c->node, &clients); pthread_mutex_unlock(&client_lock); } /* * kill off a dead client */ static void dead_client(struct client *c) { pthread_mutex_lock(&client_lock); list_del_init(&c->node); pthread_mutex_unlock(&client_lock); close(c->fd); c->fd = -1; FREE(c); } void free_polls (void) { if (polls) FREE(polls); } void check_timeout(struct timeval start_time, char *inbuf, unsigned int timeout) { struct timeval diff_time, end_time; if (start_time.tv_sec && gettimeofday(&end_time, NULL) == 0) { timersub(&end_time, &start_time, &diff_time); unsigned long msecs; msecs = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000; if (msecs > timeout) condlog(2, "cli cmd '%s' timeout reached " "after %lu.%06lu secs", inbuf, diff_time.tv_sec, diff_time.tv_usec); } } void uxsock_cleanup(void *arg) { cli_exit(); free_polls(); } /* * entry point */ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) { int ux_sock; size_t len; int rlen, timeout; char *inbuf; char *reply; sigset_t mask; ux_sock = ux_socket_listen(DEFAULT_SOCKET); if (ux_sock == -1) { condlog(1, "could not create uxsock: %d", errno); return NULL; } if (!conf) { condlog(1, "configuration changed"); return NULL; } timeout = conf->uxsock_timeout; pthread_cleanup_push(uxsock_cleanup, NULL); polls = (struct pollfd *)MALLOC(0); pthread_sigmask(SIG_SETMASK, NULL, &mask); sigdelset(&mask, SIGHUP); sigdelset(&mask, SIGUSR1); while (1) { struct pollfd *new; struct client *c, *tmp; int i, poll_count, num_clients; /* * Store configuration timeout; * configuration might change during * the call to 'reconfigure'. */ if (conf) timeout = conf->uxsock_timeout; /* setup for a poll */ pthread_mutex_lock(&client_lock); num_clients = 0; list_for_each_entry(c, &clients, node) { num_clients++; } new = REALLOC(polls, (1+num_clients) * sizeof(*polls)); /* If we can't allocate poliing space for the new client, * close it */ if (!new) { if (!num_clients) { condlog(1, "can't listen for new clients"); return NULL; } dead_client(list_entry(clients.prev, typeof(struct client), node)); } else polls = new; polls[0].fd = ux_sock; polls[0].events = POLLIN; /* setup the clients */ i = 1; list_for_each_entry(c, &clients, node) { polls[i].fd = c->fd; polls[i].events = POLLIN; i++; } pthread_mutex_unlock(&client_lock); /* most of our life is spent in this call */ poll_count = ppoll(polls, i, &sleep_time, &mask); if (poll_count == -1) { if (errno == EINTR) { handle_signals(); continue; } /* something went badly wrong! */ condlog(0, "poll"); pthread_exit(NULL); } if (poll_count == 0) continue; /* see if a client wants to speak to us */ for (i = 1; i < num_clients + 1; i++) { if (polls[i].revents & POLLIN) { struct timeval start_time; c = NULL; pthread_mutex_lock(&client_lock); list_for_each_entry(tmp, &clients, node) { if (tmp->fd == polls[i].fd) { c = tmp; break; } } pthread_mutex_unlock(&client_lock); if (!c) { condlog(3, "cli%d: invalid fd %d", i, polls[i].fd); continue; } if (gettimeofday(&start_time, NULL) != 0) start_time.tv_sec = 0; if (recv_packet(c->fd, &inbuf, &len, timeout) != 0) { dead_client(c); } else { inbuf[len - 1] = 0; condlog(4, "Got request [%s]", inbuf); uxsock_trigger(inbuf, &reply, &rlen, trigger_data); if (reply) { if (send_packet(c->fd, reply, rlen) != 0) { dead_client(c); } condlog(4, "Reply [%d bytes]", rlen); FREE(reply); reply = NULL; } check_timeout(start_time, inbuf, timeout); FREE(inbuf); } } } /* see if we got a new client */ if (polls[0].revents & POLLIN) { new_client(ux_sock); } } pthread_cleanup_pop(1); close(ux_sock); return NULL; } multipath-tools-0.5.0+git1.656f8865/multipathd/uxlsnr.h000066400000000000000000000004341260771327300224310ustar00rootroot00000000000000#ifndef _UXLSNR_H #define _UXLSNR_H typedef int (uxsock_trigger_fn)(char *, char **, int *, void *); void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data); extern volatile sig_atomic_t reconfig_sig; extern volatile sig_atomic_t log_reset_sig; #endif