pax_global_header00006660000000000000000000000064113757436660014534gustar00rootroot0000000000000052 comment=17f14138e13b3ecbb37b86199739072f2c44c1a2 multipath-tools-0.4.9/000077500000000000000000000000001137574366600147135ustar00rootroot00000000000000multipath-tools-0.4.9/.gitignore000066400000000000000000000001001137574366600166720ustar00rootroot00000000000000*.o .dotest *~ *.so *.so.0 *.a *.gz kpartx multipath multipathd multipath-tools-0.4.9/AUTHOR000066400000000000000000000000651137574366600156410ustar00rootroot00000000000000Christophe Varoqui, multipath-tools-0.4.9/COPYING000066400000000000000000000613041137574366600157520ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [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.4.9/ChangeLog000066400000000000000000000003031137574366600164610ustar00rootroot00000000000000Change 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.4.9/FAQ000066400000000000000000000071301137574366600152460ustar00rootroot00000000000000More 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.4.9/Makefile000066400000000000000000000025431137574366600163570ustar00rootroot00000000000000# 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 \ multipath \ multipathd \ 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 release: sed -e "s/__VERSION__/${VERSION}/" \ multipath-tools.spec.in > multipath-tools.spec rpm: release rpmbuild -bb multipath-tools.spec multipath-tools-0.4.9/Makefile.inc000066400000000000000000000017331137574366600171270ustar00rootroot00000000000000# 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 prefix = exec_prefix = $(prefix) bindir = $(exec_prefix)/sbin libudevdir = ${prefix}/lib/udev multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 man5dir = $(prefix)/usr/share/man/man5 rcdir = $(prefix)/etc/init.d syslibdir = $(prefix)/$(LIB) libdir = $(prefix)/$(LIB)/multipath GZIP = /bin/gzip -9 -c INSTALL_PROGRAM = install OPTFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\" SHARED_FLAGS = -shared %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< multipath-tools-0.4.9/README000066400000000000000000000000171137574366600155710ustar00rootroot00000000000000Things to read multipath-tools-0.4.9/TODO000066400000000000000000000000151137574366600153770ustar00rootroot00000000000000Things to do multipath-tools-0.4.9/getuid/000077500000000000000000000000001137574366600161745ustar00rootroot00000000000000multipath-tools-0.4.9/getuid/usb_id000077500000000000000000000003251137574366600173670ustar00rootroot00000000000000#!/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.4.9/kpartx/000077500000000000000000000000001137574366600162245ustar00rootroot00000000000000multipath-tools-0.4.9/kpartx/ChangeLog000066400000000000000000000004401137574366600177740ustar00rootroot00000000000000002: * 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.4.9/kpartx/Makefile000066400000000000000000000017721137574366600176730ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc CFLAGS += -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 LDFLAGS = -ldevmapper OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \ gpt.o mac.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 clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.4.9/kpartx/README000066400000000000000000000002531137574366600171040ustar00rootroot00000000000000This 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.4.9/kpartx/bsd.c000066400000000000000000000073601137574366600171460ustar00rootroot00000000000000#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.4.9/kpartx/byteorder.h000066400000000000000000000010541137574366600203740ustar00rootroot00000000000000#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 be32_to_cpu(x) bswap_32(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 be32_to_cpu(x) (x) #else # error unsupported #endif #endif /* BYTEORDER_H_INCLUDED */ multipath-tools-0.4.9/kpartx/crc32.c000066400000000000000000000315351137574366600173130ustar00rootroot00000000000000/* * 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.4.9/kpartx/crc32.h000066400000000000000000000010431137574366600173070ustar00rootroot00000000000000/* * 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.4.9/kpartx/dasd.c000066400000000000000000000143441137574366600173110ustar00rootroot00000000000000/* * 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); } /* */ 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 */ unsigned int *label = (unsigned int *) &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.4.9/kpartx/dasd.h000066400000000000000000000302361137574366600173140ustar00rootroot00000000000000/* * 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 */ char 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.4.9/kpartx/devmapper.c000066400000000000000000000113601137574366600203540ustar00rootroot00000000000000/* * 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 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, uint32_t *cookie) { int r = 0; int udev_wait_flag = (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE); 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); if (udev_wait_flag && !dm_task_set_cookie(dmt, cookie, 0)) goto out; r = dm_task_run(dmt); 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, const char *uuid, int part, mode_t mode, uid_t uid, gid_t gid, uint32_t *cookie) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; 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 (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); if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, cookie, 0)) goto addout; r = dm_task_run (dmt); addout: dm_task_destroy (dmt); 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; } multipath-tools-0.4.9/kpartx/devmapper.h000066400000000000000000000011521137574366600203570ustar00rootroot00000000000000#define MAJOR(dev) ((dev & 0xfff00) >> 8) #define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00)) #define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12)) int dm_prereq (char *, int, int, int); int dm_simplecmd (int, const char *, int, uint32_t *); int dm_addmap (int, const char *, const char *, const char *, uint64_t, const char *, int, mode_t, uid_t, gid_t, uint32_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); multipath-tools-0.4.9/kpartx/dos.c000066400000000000000000000040671137574366600171640ustar00rootroot00000000000000/* * 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; next = start = 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 + le32_to_cpu(p.start_sect); moretodo = 1; } continue; } if (n < ns) { sp[n].start = here + le32_to_cpu(p.start_sect); sp[n].size = 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; 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 = le32_to_cpu(p.start_sect); sp[i].size = le32_to_cpu(p.nr_sects); } else { fprintf(stderr, "dos_partition: too many slices\n"); break; } if (is_extended(p.sys_type)) { n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; } multipath-tools-0.4.9/kpartx/dos.h000066400000000000000000000004531137574366600171640ustar00rootroot00000000000000#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.4.9/kpartx/efi.h000066400000000000000000000034361137574366600171460ustar00rootroot00000000000000/* 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.4.9/kpartx/gpt.c000066400000000000000000000452731137574366600171750ustar00rootroot00000000000000/* 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" #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_sector_size * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * sector size, or 512. ************************************************************/ static 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; } /************************************************************ * _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; } 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; 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 = __le64_to_cpu(ptes[i].starting_lba); sp[n].size = __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.4.9/kpartx/gpt.h000066400000000000000000000073621137574366600171770ustar00rootroot00000000000000/* 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.4.9/kpartx/kpartx.8000066400000000000000000000017221137574366600176300ustar00rootroot00000000000000.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 \-d Delete partition mappings .TP .B \-l List partition mappings that would be added -a .TP .B \-p set device name-partition number delimiter .TP .B \-g force GUID partition table (GPT) .TP .B \-v Operate verbosely .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.4.9/kpartx/kpartx.c000066400000000000000000000305701137574366600177060ustar00rootroot00000000000000/* * 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 "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 }; struct pt { char *type; ptreader *fn; } pts[MAXTYPES]; int ptct = 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); } static char short_opts[] = "ladgvp:t:s"; /* Used in gpt.c */ int force_gpt=0; static int usage(void) { printf("usage : kpartx [-a|-d|-l] [-v] wholedisk\n"); printf("\t-a add partition devmappings\n"); printf("\t-d del 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-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 = (unsigned int)MAJOR(buf.st_rdev); minor = (unsigned int)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 fd, i, j, m, n, op, off, arg, c, d; 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 loopro = 0; int hotplug = 0; int loopcreated = 0; int sync = 0; struct stat buf; uint32_t cookie = 0; 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 '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': sync = 1; break; default: usage(); exit(1); } if (!sync) dm_udev_set_sync_support(0); if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE)) { 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, &loopro)) { fprintf(stderr, "can't set up loop\n"); exit (1); } loopcreated = 1; } device = loopdev; } if (delim == NULL) { delim = malloc(DELIM_SIZE); memset(delim, 0, DELIM_SIZE); set_delimiter(device, delim); } off = find_devname_offset(device); if (!loopdev) { uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), (unsigned int)MINOR(buf.st_rdev)); mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev), (unsigned int)MINOR(buf.st_rdev)); } if (!uuid) uuid = device + off; if (!mapname) mapname = device + off; 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); 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; } if (loopcreated && 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 DELETE: for (j = n-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, 0, &cookie)) { 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: 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, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid, &cookie)) { fprintf(stderr, "create/reload failed on %s\n", partname); r++; } if (op == DM_DEVICE_RELOAD && !dm_simplecmd(DM_DEVICE_RESUME, partname, 1, &cookie)) { 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++) { uint64_t start; 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); start = slices[j].start - slices[k].start; if (safe_sprintf(params, "%d:%d %" PRIu64, slices[k].major, slices[k].minor, 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, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid, &cookie); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, partname, 1, &cookie); 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; } break; default: break; } if (n > 0) break; } dm_udev_wait(cookie); 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; } multipath-tools-0.4.9/kpartx/kpartx.h000066400000000000000000000022341137574366600177070ustar00rootroot00000000000000#ifndef _KPARTX_H #define _KPARTX_H #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) /* * 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; 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.4.9/kpartx/kpartx.rules000066400000000000000000000020311137574366600206050ustar00rootroot00000000000000# # 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_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}" # Create persistent links for dmraid tables ENV{DM_UUID}=="dmraid-*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" # Create persistent links for partitions ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}" # Create dm tables for partitions ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="mpath-*", \ RUN+="/sbin/kpartx -a -p -part /dev/$name" ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="dmraid-*", \ RUN+="/sbin/kpartx -a -p -part /dev/$name" LABEL="kpartx_end" multipath-tools-0.4.9/kpartx/kpartx_id000066400000000000000000000043061137574366600201370ustar00rootroot00000000000000#!/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" # 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 dmname=$tblname # 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=dasd" ;; *\(9*) echo "DM_TYPE=raid" ;; *) echo "DM_TYPE=scsi" ;; esac else echo "DM_TYPE=raid" fi exit 0 multipath-tools-0.4.9/kpartx/lopart.c000066400000000000000000000141601137574366600176730ustar00rootroot00000000000000/* 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 #if defined(__hppa__) || defined(__powerpc64__) || defined (__alpha__) \ || defined (__x86_64__) typedef unsigned long __kernel_old_dev_t; #elif defined(__powerpc__) || defined(__ia64__) || (defined(__sparc__) && defined (__arch64__)) typedef unsigned int __kernel_old_dev_t; #else typedef unsigned short __kernel_old_dev_t; #endif #define dev_t __kernel_old_dev_t #include #include "lopart.h" #include "xstrncpy.h" #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; 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 (0 == strcmp(filename, loopinfo.lo_name)) { 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, somedev = 0, someloop = 0, loop_known = 0; struct stat statbuf; struct loop_info loopinfo; FILE *procdev; 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)) { 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 `insmod loop.o'.)", 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 `insmod loop.o'), 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) 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 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; } if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { perror ("ioctl: LOOP_CLR_FD"); close (fd); return 1; } close (fd); return 0; } multipath-tools-0.4.9/kpartx/lopart.h000066400000000000000000000003761137574366600177040ustar00rootroot00000000000000extern 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.4.9/kpartx/mac.c000066400000000000000000000021771137574366600171370ustar00rootroot00000000000000#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.4.9/kpartx/mac.h000066400000000000000000000015531137574366600171410ustar00rootroot00000000000000#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.4.9/kpartx/solaris.c000066400000000000000000000034221137574366600200450ustar00rootroot00000000000000#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.4.9/kpartx/sun.c000066400000000000000000000063421137574366600172020ustar00rootroot00000000000000/* * 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 (s->num_sectors == 0) continue; 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.4.9/kpartx/sysmacros.h000066400000000000000000000002631137574366600204210ustar00rootroot00000000000000/* 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.4.9/kpartx/unixware.c000066400000000000000000000052551137574366600202410ustar00rootroot00000000000000#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.4.9/kpartx/xstrncpy.c000066400000000000000000000003261137574366600202630ustar00rootroot00000000000000/* 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.4.9/kpartx/xstrncpy.h000066400000000000000000000000751137574366600202710ustar00rootroot00000000000000extern void xstrncpy(char *dest, const char *src, size_t n); multipath-tools-0.4.9/libmultipath/000077500000000000000000000000001137574366600174115ustar00rootroot00000000000000multipath-tools-0.4.9/libmultipath/Makefile000066400000000000000000000022241137574366600210510ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc SONAME=0 DEVLIB = libmultipath.so LIBS = $(DEVLIB).$(SONAME) LIBDEPS = -lpthread -ldl -ldevmapper 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 regex.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 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 all: $(LIBS) $(LIBS): $(OBJS) $(CC) $(SHARED_FLAGS) $(LIBDEPS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) 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.4.9/libmultipath/alias.c000066400000000000000000000216321137574366600206520ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "uxsock.h" #include "alias.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 bindings file pathname : %s", 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_bindings_file(int fd) { 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); sigprocmask(SIG_UNBLOCK, &set, &oldset); alarm(BINDINGS_FILE_TIMEOUT); err = fcntl(fd, F_SETLKW, &lock); alarm(0); if (err) { if (errno != EINTR) condlog(0, "Cannot lock bindings file : %s", strerror(errno)); else condlog(0, "Bindings file is locked. Giving up."); } sigprocmask(SIG_SETMASK, &oldset, NULL); sigaction(SIGALRM, &oldact, NULL); return err; } static int open_bindings_file(char *file, int *can_write) { 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 bindings file [%s] read/write. " " trying readonly", file); fd = open(file, O_RDONLY); if (fd < 0) { condlog(0, "Cannot open bindings file [%s] " "readonly : %s", file, strerror(errno)); return -1; } } else { condlog(0, "Cannot open bindings file [%s] : %s", file, strerror(errno)); return -1; } } if (*can_write && lock_bindings_file(fd) < 0) goto fail; memset(&s, 0, sizeof(s)); if (fstat(fd, &s) < 0){ condlog(0, "Cannot stat bindings file : %s", strerror(errno)); goto fail; } if (s.st_size == 0) { if (*can_write == 0) goto fail; /* If bindings file is empty, write the header */ size_t len = strlen(BINDINGS_FILE_HEADER); if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) { condlog(0, "Cannot write header to bindings file : %s", strerror(errno)); /* cleanup partially written header */ ftruncate(fd, 0); goto fail; } fsync(fd); condlog(3, "Initialized new bindings file [%s]", file); } return fd; fail: close(fd); return -1; } static int format_devname(char *name, int id, int len) { int pos; memset(name,0, len); strcpy(name,"mpath"); for (pos = len - 1; pos >= 5; pos--) { name[pos] = 'a' + id % 26; if (id < 26) break; id /= 26; id--; } memmove(name + 5, name + pos, len - pos); name[5 + len - pos] = '\0'; return (5 + len - pos); } static int scan_devname(char *alias) { char *c; int i, n = 0; if (strncmp(alias, "mpath", 5)) return -1; c = alias + 5; while (*c != '\0' && *c != ' ' && *c != '\t') { i = *c - 'a'; n = ( n * 26 ) + i; c++; if (*c < 'a' || *c > 'z') break; n++; } return n; } static int lookup_binding(FILE *f, char *map_wwid, char **map_alias) { char buf[LINE_MAX]; unsigned int line_nr = 0; int id = 0; *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); if (curr_id >= id) id = curr_id + 1; wwid = strtok(NULL, ""); 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 id; } } condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); return id; } static int rlookup_binding(FILE *f, char **map_wwid, char *map_alias) { char buf[LINE_MAX]; unsigned int line_nr = 0; int id = 0; *map_wwid = 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); if (curr_id >= id) id = curr_id + 1; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, "Ignoring malformed line %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); *map_wwid = strdup(wwid); if (*map_wwid == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); return id; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); return id; } static char * allocate_binding(int fd, char *wwid, int id) { 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); 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 */ ftruncate(fd, offset); 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 * get_user_friendly_alias(char *wwid, char *file) { char *alias; int fd, scan_fd, id; FILE *f; int can_write; if (!wwid || *wwid == '\0') { condlog(3, "Cannot find binding for empty WWID"); return NULL; } fd = open_bindings_file(file, &can_write); if (fd < 0) return NULL; scan_fd = dup(fd); if (scan_fd < 0) { condlog(0, "Cannot dup bindings file descriptor : %s", strerror(errno)); close(fd); return NULL; } f = fdopen(scan_fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(scan_fd); close(fd); return NULL; } id = lookup_binding(f, wwid, &alias); if (id < 0) { fclose(f); close(scan_fd); close(fd); return NULL; } if (!alias && can_write) alias = allocate_binding(fd, wwid, id); fclose(f); close(scan_fd); close(fd); return alias; } char * get_user_friendly_wwid(char *alias, char *file) { char *wwid; int fd, scan_fd, id, unused; FILE *f; if (!alias || *alias == '\0') { condlog(3, "Cannot find binding for empty alias"); return NULL; } fd = open_bindings_file(file, &unused); if (fd < 0) return NULL; scan_fd = dup(fd); if (scan_fd < 0) { condlog(0, "Cannot dup bindings file descriptor : %s", strerror(errno)); close(fd); return NULL; } f = fdopen(scan_fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(scan_fd); close(fd); return NULL; } id = rlookup_binding(f, &wwid, alias); if (id < 0) { fclose(f); close(scan_fd); close(fd); return NULL; } fclose(f); close(scan_fd); close(fd); return wwid; } multipath-tools-0.4.9/libmultipath/alias.h000066400000000000000000000006371137574366600206610ustar00rootroot00000000000000#define BINDINGS_FILE_TIMEOUT 30 #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 *get_user_friendly_wwid(char *alias, char *file); multipath-tools-0.4.9/libmultipath/blacklist.c000066400000000000000000000150541137574366600215320ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #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); return 1; } ble->vendor = vendor; } if (product) { if (regcomp(&ble->product_reg, product, REG_EXTENDED|REG_NOSUB)) { FREE(product); return 1; } ble->product = product; } ble->origin = origin; 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("^hd[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; vector_foreach_slot (conf->hwtable, hwe, i) { if (hwe->bl_product) { 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); return 1; } } } return 0; } int _blacklist_exceptions (vector elist, 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, 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 (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && !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 (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; } return 0; } #define LOG_BLIST(M) \ if (vendor && product) \ condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \ else if (wwid) \ condlog(3, "%s: (%s) %s", dev, wwid, (M)); \ else \ condlog(3, "%s: %s", dev, (M)) void log_filter (char *dev, char *vendor, char *product, char *wwid, 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_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; } } 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, 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, 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) { int r = _filter_wwid(blist, elist, wwid); log_filter(NULL, NULL, NULL, wwid, 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, r); return r; } 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.4.9/libmultipath/blacklist.h000066400000000000000000000017141137574366600215350ustar00rootroot00000000000000#ifndef _BLACKLIST_H #define _BLACKLIST_H #include "regex.h" #define MATCH_NOTHING 0 #define MATCH_WWID_BLIST 1 #define MATCH_DEVICE_BLIST 2 #define MATCH_DEVNODE_BLIST 3 #define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST #define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST #define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_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 *); int filter_device (vector, vector, char *, char *); int filter_path (struct config *, struct path *); 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.4.9/libmultipath/callout.c000066400000000000000000000072371137574366600212310ustar00rootroot00000000000000/* * 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 "debug.h" #define PROGRAM_SIZE 100 #define FIELD_PROGRAM #define strfieldcpy(to, from) \ do { \ to[sizeof(to)-1] = '\0'; \ strncpy(to, from, sizeof(to)-1); \ } while (0) 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[PROGRAM_SIZE]; char *argv[sizeof(arg) / 2]; int i; i = 0; if (strchr(path, ' ')) { strfieldcpy(arg, path); pos = arg; while (pos != NULL) { 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); 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 exitted 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.4.9/libmultipath/callout.h000066400000000000000000000002341137574366600212240ustar00rootroot00000000000000#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.4.9/libmultipath/checkers.c000066400000000000000000000071031137574366600213450ustar00rootroot00000000000000#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" }; 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) { return MALLOC(sizeof(struct checker)); } void free_checker (struct checker * c) { 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) { list_del(&checker_loop->node); free_checker(checker_loop); } } struct checker * checker_lookup (char * name) { struct checker * c; 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]; void * handle; struct checker * c; char *errstr; c = alloc_checker(); if (!c) return NULL; snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so", conf->multipath_dir, name); condlog(3, "loading %s checker", libname); handle = dlopen(libname, RTLD_NOW); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!handle) goto out; c->check = (int (*)(struct checker *)) dlsym(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(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(handle, "libcheck_free"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->free) goto out; snprintf(c->name, CHECKER_NAME_LEN, "%s", name); 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) { c->fd = fd; } void checker_set_sync (struct checker * c) { c->sync = 1; } void checker_set_async (struct checker * c) { c->sync = 0; } void checker_enable (struct checker * c) { c->disable = 0; } void checker_disable (struct checker * c) { c->disable = 1; } int checker_init (struct checker * c, void ** mpctxt_addr) { c->mpcontext = mpctxt_addr; return c->init(c); } void checker_put (struct checker * c) { if (c->free) c->free(c); memset(c, 0x0, sizeof(struct checker)); } int checker_check (struct checker * c) { int r; if (c->disable) 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) { return (c->check) ? 1 : 0; } char * checker_name (struct checker * c) { return c->name; } char * checker_message (struct checker * c) { return c->message; } void checker_get (struct checker * dst, char * name) { struct checker * src = checker_lookup(name); 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; } multipath-tools-0.4.9/libmultipath/checkers.h000066400000000000000000000101371137574366600213530ustar00rootroot00000000000000#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. */ enum path_check_state { PATH_WILD, PATH_UNCHECKED, PATH_DOWN, PATH_UP, PATH_SHAKY, PATH_GHOST, PATH_PENDING, 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 /* * Overloaded storage response time can be very long. * SG_IO timouts after DEF_TIMEOUT milliseconds, and checkers interprets this * as a path failure. multipathd then proactively evicts the path from the DM * multipath table in this case. * * This generaly snow balls and ends up in full eviction and IO errors for end * users. Bad. This may also cause SCSI bus resets, causing disruption for all * local and external storage hardware users. * * Provision a long timeout. Longer than any real-world application would cope * with. */ #define DEF_TIMEOUT 300000 #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; int fd; int sync; 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_get (struct checker *, char *); #endif /* _CHECKERS_H */ multipath-tools-0.4.9/libmultipath/checkers/000077500000000000000000000000001137574366600212005ustar00rootroot00000000000000multipath-tools-0.4.9/libmultipath/checkers/Makefile000066400000000000000000000011631137574366600226410ustar00rootroot00000000000000# 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) $(SHARED_FLAGS) -o $@ $^ -laio libcheck%.so: libsg.o %.o $(CC) $(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.4.9/libmultipath/checkers/cciss.h000066400000000000000000000057231137574366600224640ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/cciss_tur.c000066400000000000000000000077261137574366600233560ustar00rootroot00000000000000/* ***************************************************************************** * * * (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.4.9/libmultipath/checkers/directio.c000066400000000000000000000101051137574366600231430ustar00rootroot00000000000000/* * 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) { 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) { LOG(4, "called in synchronous mode"); timeout.tv_sec = ASYNC_TIMEOUT_SEC; 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)); rc = PATH_UNCHECKED; } else if (r < 1L) { if (ct->running > ASYNC_TIMEOUT_SEC || sync) { LOG(3, "abort check on timeout"); 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); 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.4.9/libmultipath/checkers/directio.h000066400000000000000000000002611137574366600231520ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/emc_clariion.c000066400000000000000000000140421137574366600237710ustar00rootroot00000000000000/* * 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" #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 = DEF_TIMEOUT; 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], sbb = &sb[0]); 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); MSG(c, "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); MSG(c, "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.4.9/libmultipath/checkers/emc_clariion.h000066400000000000000000000003111137574366600237700ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/hp_sw.c000066400000000000000000000064211137574366600224670ustar00rootroot00000000000000/* * 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 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 do_tur (int fd) { 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 = DEF_TIMEOUT; 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)) { MSG(c, MSG_HP_SW_DOWN); return PATH_DOWN; } if (do_tur(c->fd)) { MSG(c, MSG_HP_SW_GHOST); return PATH_GHOST; } MSG(c, MSG_HP_SW_UP); return PATH_UP; } multipath-tools-0.4.9/libmultipath/checkers/hp_sw.h000066400000000000000000000002371137574366600224730ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/libsg.c000066400000000000000000000043161137574366600224500ustar00rootroot00000000000000/* * 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, unsigned char * senseBuff) { /* defaults */ int blocks = 1; long long start_block = 0; int bs = 512; int cdbsz = 10; int * diop = NULL; unsigned char rdCmd[cdbsz]; unsigned char *sbb = senseBuff; 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; 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_BUFF_LEN; io_hdr.sbp = senseBuff; io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = (int)start_block; if (diop && *diop) io_hdr.flags |= SG_FLAG_DIRECT_IO; retry: memset(senseBuff, 0, SENSE_BUFF_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.4.9/libmultipath/checkers/libsg.h000066400000000000000000000002371137574366600224530ustar00rootroot00000000000000#ifndef _LIBSG_H #define _LIBSG_H #define SENSE_BUFF_LEN 32 int sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff); #endif /* _LIBSG_H */ multipath-tools-0.4.9/libmultipath/checkers/rdac.c000066400000000000000000000056331137574366600222640ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/sg_include.h" #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 #define RDAC_DEF_TIMEOUT 60000 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #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" struct rdac_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, unsigned int pg_op, void *resp, int mx_resp_len) { 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; 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 = RDAC_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; } struct volume_access_inq { char PQ_PDT; char dontcare0[7]; char avtcvp; char dontcare1[39]; }; extern int libcheck_check (struct checker * c) { struct volume_access_inq inq; int ret; memset(&inq, 0, sizeof(struct volume_access_inq)); if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq))) { ret = PATH_DOWN; goto done; } else if ((inq.PQ_PDT & 0x20) || (inq.PQ_PDT & 0x7f)) { /* LUN not connected*/ ret = PATH_DOWN; goto done; } ret = ((inq.avtcvp & 0x1) ? PATH_UP : PATH_GHOST); done: switch (ret) { case PATH_DOWN: MSG(c, MSG_RDAC_DOWN); break; case PATH_UP: MSG(c, MSG_RDAC_UP); break; case PATH_GHOST: MSG(c, MSG_RDAC_GHOST); break; } return ret; } multipath-tools-0.4.9/libmultipath/checkers/rdac.h000066400000000000000000000002261137574366600222620ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/readsector0.c000066400000000000000000000013531137574366600235610ustar00rootroot00000000000000/* * 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], &sbuf[0]); 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.4.9/libmultipath/checkers/readsector0.h000066400000000000000000000003031137574366600235600ustar00rootroot00000000000000#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.4.9/libmultipath/checkers/tur.c000066400000000000000000000047171137574366600221670ustar00rootroot00000000000000/* * Some code borrowed from sg-utils. * * Copyright (c) 2004 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 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" struct 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) { 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 = DEF_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, MSG_TUR_DOWN); return PATH_DOWN; } 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 */ MSG(c, MSG_TUR_GHOST); return PATH_GHOST; } } MSG(c, MSG_TUR_DOWN); return PATH_DOWN; } MSG(c, MSG_TUR_UP); return PATH_UP; } multipath-tools-0.4.9/libmultipath/checkers/tur.h000066400000000000000000000002231137574366600221600ustar00rootroot00000000000000#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.4.9/libmultipath/config.c000066400000000000000000000224641137574366600210320ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #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" static int hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) { if (hwe1->vendor && hwe2->vendor && strcmp(hwe1->vendor, hwe2->vendor)) return 1; if (hwe1->product && hwe2->product && strcmp(hwe1->product, hwe2->product)) return 1; if (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; } struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char * revision) { int i; struct hwentry *hwe, *ret = NULL; regex_t vre, pre, rre; vector_foreach_slot (hwtable, hwe, i) { if (hwe->vendor && regcomp(&vre, hwe->vendor, REG_EXTENDED|REG_NOSUB)) break; if (hwe->product && regcomp(&pre, hwe->product, REG_EXTENDED|REG_NOSUB)) { regfree(&vre); break; } if (hwe->revision && regcomp(&rre, hwe->revision, REG_EXTENDED|REG_NOSUB)) { regfree(&vre); regfree(&pre); break; } if ((!hwe->vendor || !regexec(&vre, vendor, 0, NULL, 0)) && (!hwe->product || !regexec(&pre, product, 0, NULL, 0)) && (!hwe->revision || !regexec(&rre, revision, 0, NULL, 0))) ret = hwe; if (hwe->revision) regfree(&rre); if (hwe->product) regfree(&pre); if (hwe->vendor) regfree(&vre); if (ret) 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->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->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->alias) FREE(mpe->alias); 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 (hwe2->s) { \ if (hwe1->s) \ FREE(hwe1->s); \ if (!(hwe1->s = set_param_str(hwe2->s))) \ return 1; \ } #define merge_num(s) \ if (hwe2->s) \ hwe1->s = hwe2->s static int merge_hwe (struct hwentry * hwe1, struct hwentry * hwe2) { merge_str(vendor); merge_str(product); merge_str(revision); merge_str(getuid); merge_str(features); merge_str(hwhandler); merge_str(selector); merge_str(checker_name); merge_str(prio_name); merge_str(prio_args); merge_str(bl_product); merge_num(pgpolicy); merge_num(pgfailback); merge_num(rr_weight); merge_num(no_path_retry); merge_num(minio); 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->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; 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; 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 int factorize_hwtable (vector hw) { struct hwentry *hwe1, *hwe2; int i, j; vector_foreach_slot(hw, hwe1, i) { j = i+1; vector_foreach_slot_after(hw, hwe2, j) { if (hwe_strmatch(hwe1, hwe2)) continue; /* dup */ merge_hwe(hwe1, hwe2); free_hwe(hwe2); vector_del_slot(hw, j); j--; } } return 0; } 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->udev_dir) FREE(conf->udev_dir); if (conf->multipath_dir) FREE(conf->multipath_dir); if (conf->selector) FREE(conf->selector); if (conf->getuid) FREE(conf->getuid); if (conf->features) FREE(conf->features); if (conf->hwhandler) FREE(conf->hwhandler); if (conf->prio_name) FREE(conf->prio_name); if (conf->prio_args) FREE(conf->prio_args); if (conf->checker_name) FREE(conf->checker_name); free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); free_blacklist_device(conf->blist_device); free_blacklist(conf->elist_devnode); free_blacklist(conf->elist_wwid); free_blacklist_device(conf->elist_device); free_mptable(conf->mptable); free_hwtable(conf->hwtable); free_keywords(conf->keywords); FREE(conf); } int load_config (char * file) { if (!conf) conf = alloc_config(); if (!conf) return 1; /* * internal defaults */ if (!conf->verbosity) conf->verbosity = DEFAULT_VERBOSITY; conf->dev_type = DEV_NONE; conf->minio = 1000; conf->max_fds = 0; conf->bindings_file = DEFAULT_BINDINGS_FILE; conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR); conf->flush_on_last_del = 0; conf->attribute_flags = 0; /* * 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 */ if (filepresent(file)) { set_current_keywords(&conf->keywords); if (init_data(file, init_keywords)) { condlog(0, "error parsing config file"); goto out; } } /* * remove duplica in hwtable. config file takes precedence * over build-in hwtable */ factorize_hwtable(conf->hwtable); /* * fill the voids left in the config file */ 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 (setup_default_blist(conf)) 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->mptable == NULL) { conf->mptable = vector_alloc(); if (!conf->mptable) goto out; } if (conf->udev_dir == NULL) conf->udev_dir = set_default(DEFAULT_UDEVDIR); if (!conf->udev_dir || !conf->multipath_dir) goto out; return 0; out: free_config(conf); return 1; } multipath-tools-0.4.9/libmultipath/config.h000066400000000000000000000044651137574366600210400ustar00rootroot00000000000000#ifndef _CONFIG_H #define _CONFIG_H #include #include #define ORIGIN_DEFAULT 0 #define ORIGIN_CONFIG 1 enum devtypes { DEV_NONE, DEV_DEVT, DEV_DEVNODE, DEV_DEVMAP }; struct hwentry { char * vendor; char * product; char * revision; char * getuid; char * features; char * hwhandler; char * selector; char * checker_name; char * prio_name; char * prio_args; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int pg_timeout; int flush_on_last_del; int fast_io_fail; unsigned int dev_loss; char * bl_product; }; struct mpentry { char * wwid; char * alias; char * getuid; char * selector; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int pg_timeout; int flush_on_last_del; int attribute_flags; uid_t uid; gid_t gid; mode_t mode; }; struct config { int verbosity; int dry_run; int list; int pgpolicy_flag; int with_sysfs; int pgpolicy; enum devtypes dev_type; int minio; int checkint; int max_checkint; int pgfailback; int remove; int rr_weight; int no_path_retry; int user_friendly_names; int pg_timeout; int max_fds; int force_reload; int queue_without_daemon; int daemon; int flush_on_last_del; int attribute_flags; int fast_io_fail; unsigned int dev_loss; uid_t uid; gid_t gid; mode_t mode; uint32_t cookie; char * dev; char * sysfs_dir; char * udev_dir; char * multipath_dir; char * selector; char * getuid; char * features; char * hwhandler; char * bindings_file; char * prio_name; char * prio_args; char * checker_name; vector keywords; vector mptable; vector hwtable; vector blist_devnode; vector blist_wwid; vector blist_device; vector elist_devnode; vector elist_wwid; vector elist_device; }; 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 config * alloc_config (void); void free_config (struct config * conf); #endif multipath-tools-0.4.9/libmultipath/configure.c000066400000000000000000000332561137574366600215470ustar00rootroot00000000000000/* * 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 "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" extern int setup_map (struct multipath * mpp) { 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_pg_timeout(mpp); select_mode(mpp); select_uid(mpp); select_gid(mpp); select_fast_io_fail(mpp); select_dev_loss(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); /* * ponders each path group and determine highest prio pg * to switch over (default to first) */ mpp->bestpg = select_path_group(mpp); /* * transform the mp->pg vector of vectors of paths * into a mp->params strings to feed the device-mapper */ if (assemble_map(mpp)) { 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) { 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; cmpp = find_mp_by_alias(curmp, mpp->alias); if (!cmpp) { cmpp = find_mp_by_wwid(curmp, mpp->wwid); 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; return; } mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map does not exist)", mpp->alias); return; } if (!find_mp_by_wwid(curmp, mpp->wwid)) { condlog(2, "%s: remove (wwid changed)", cmpp->alias); dm_flush_map(mpp->alias); strncat(cmpp->wwid, mpp->wwid, WWID_SIZE); drop_multipath(curmp, cmpp->wwid, KEEP_PATHS); mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map wwid change)", mpp->alias); 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_RELOAD; condlog(3, "%s: set ACT_RELOAD (size change)", mpp->alias); return; } if (!mpp->no_path_retry && !mpp->pg_timeout && (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 (!cmpp->selector || 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; 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) return 1; else if (!lock) flock(pp->fd, LOCK_UN); } } return 0; } /* * 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) { int r = 0; /* * last chance to quit before touching the devmaps */ if (conf->dry_run && mpp->action != ACT_NOTHING) { print_multipath_topology(mpp, conf->verbosity); return DOMAP_DRY; } 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; } if (dm_map_present(mpp->alias)) { condlog(3, "%s: map already present", mpp->alias); lock_multipath(mpp, 0); break; } r = dm_addmap_create(mpp); if (!r) r = dm_addmap_create_ro(mpp); lock_multipath(mpp, 0); break; case ACT_RELOAD: r = dm_addmap_reload(mpp); if (!r) r = dm_addmap_reload_ro(mpp); if (r) r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias); break; case ACT_RESIZE: r = dm_addmap_reload(mpp); if (!r) r = dm_addmap_reload_ro(mpp); if (r) r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1); break; case ACT_RENAME: r = dm_rename(mpp->alias_old, mpp->alias); break; default: break; } if (r) { /* * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ if (!conf->daemon) { /* multipath client mode */ dm_switchgroup(mpp->alias, mpp->bestpg); if (mpp->action != ACT_NOTHING) print_multipath_topology(mpp, conf->verbosity); } else { /* multipath daemon mode */ mpp->stat_map_loads++; condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, TGT_MPATH, mpp->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; } 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 */ } extern int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_reload) { int r = 1; int k, i; char empty_buff[WWID_SIZE]; struct multipath * mpp; struct path * pp1; struct path * pp2; vector curmp = vecs->mpvec; vector pathvec = vecs->pathvec; memset(empty_buff, 0, WWID_SIZE); 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 (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 || filter_path(conf, pp1) > 0) continue; /* 2. if path already coalesced */ if (pp1->mpp) continue; /* 3. if path has disappeared */ if (!pp1->size) continue; /* 4. path is out of scope */ if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE)) 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 (!pp2->size) continue; if (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, NULL); if (setup_map(mpp)) { remove_map(mpp, vecs, 0); continue; } if (mpp->action == ACT_UNDEF) select_action(mpp, curmp, force_reload); r = domap(mpp); if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for create/reload map", mpp->alias, r); if (r == DOMAP_FAIL) { remove_map(mpp, vecs, 0); continue; } else /* if (r == DOMAP_RETRY) */ return r; } if (r == DOMAP_DRY) continue; 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); } if (mpp->pg_timeout != PGTIMEOUT_UNDEF) { if (mpp->pg_timeout == -PGTIMEOUT_NONE) dm_set_pg_timeout(mpp->alias, 0); else dm_set_pg_timeout(mpp->alias, mpp->pg_timeout); } 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(mpp->alias)) condlog(2, "%s: remove failed (dead)", mpp->alias); else condlog(2, "%s: remove (dead)", mpp->alias); } } return 0; } extern char * get_refwwid (char * dev, enum devtypes dev_type, vector pathvec) { struct path * pp; char buff[FILE_NAME_SIZE]; char * refwwid = NULL, tmpwwid[WWID_SIZE]; if (dev_type == DEV_NONE) return NULL; if (dev_type == DEV_DEVNODE) { basenamecpy(dev, buff); pp = find_path_by_dev(pathvec, buff); if (!pp) { pp = alloc_path(); if (!pp) return NULL; strncpy(pp->dev, buff, FILE_NAME_SIZE); if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID)) return NULL; if (store_path(pathvec, pp)) { free_path(pp); return NULL; } } refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVT) { pp = find_path_by_devt(pathvec, dev); if (!pp) { if (devt2devname(buff, dev)) return NULL; pp = alloc_path(); if (!pp) return NULL; strncpy(pp->dev, buff, FILE_NAME_SIZE); if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID)) return NULL; if (store_path(pathvec, pp)) { free_path(pp); return NULL; } } refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVMAP) { if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { refwwid = tmpwwid; goto out; } /* * may be a binding */ refwwid = get_user_friendly_wwid(dev, conf->bindings_file); if (refwwid) return refwwid; /* * or may be an alias */ refwwid = get_mpe_wwid(dev); /* * or directly a wwid */ if (!refwwid) refwwid = dev; } out: if (refwwid && strlen(refwwid)) return STRDUP(refwwid); return NULL; } multipath-tools-0.4.9/libmultipath/configure.h000066400000000000000000000014101137574366600215370ustar00rootroot00000000000000/* * 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, }; #define FLUSH_ONE 1 #define FLUSH_ALL 2 int setup_map (struct multipath * mpp); int domap (struct multipath * mpp); int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload); char * get_refwwid (char * dev, enum devtypes dev_type, vector pathvec); multipath-tools-0.4.9/libmultipath/debug.c000066400000000000000000000012601137574366600206420ustar00rootroot00000000000000/* * 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) { 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.4.9/libmultipath/debug.h000066400000000000000000000004061137574366600206500ustar00rootroot00000000000000void 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.4.9/libmultipath/defaults.c000066400000000000000000000003731137574366600213670ustar00rootroot00000000000000/* * 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.4.9/libmultipath/defaults.h000066400000000000000000000016011137574366600213670ustar00rootroot00000000000000#define DEFAULT_GETUID "/lib/udev/scsi_id --whitelisted --device=/dev/%n" #define DEFAULT_UDEVDIR "/dev" #define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath" #define DEFAULT_SELECTOR "round-robin 0" #define DEFAULT_FEATURES "0" #define DEFAULT_HWHANDLER "0" #define DEFAULT_MINIO 1000 #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_PGTIMEOUT -PGTIMEOUT_NONE #define DEFAULT_USER_FRIENDLY_NAMES 0 #define DEFAULT_VERBOSITY 2 #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) #define DEFAULT_PIDFILE "/var/run/multipathd.pid" #define DEFAULT_SOCKET "/var/run/multipathd.sock" #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" char * set_default (char * str); multipath-tools-0.4.9/libmultipath/devmapper.c000066400000000000000000000465731137574366600215570ustar00rootroot00000000000000/* * 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 "log_pthread.h" #include #include #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 #define UUID_PREFIX "mpath-" #define UUID_PREFIX_LEN 6 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) { 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_libprereq (void) { char version[64]; int v[3]; int minv[3] = {1, 2, 38}; 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 ((v[0] > minv[0]) || ((v[0] == minv[0]) && (v[1] > minv[1])) || ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) return 0; condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d", minv[0], minv[1], minv[2]); return 1; } static int dm_drvprereq (char * str) { int r = 2; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; int minv[3] = {1, 0, 3}; unsigned int *v; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 3; 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 multipath kernel driver not loaded"); goto out; } v = target->version; if ((v[0] > minv[0]) || ((v[0] == minv[0]) && (v[1] > minv[1])) || ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) { r = 0; goto out; } condlog(0, "DM multipath kernel driver must be >= %u.%.2u.%.2u", minv[0], minv[1], minv[2]); out: dm_task_destroy(dmt); return r; } extern int dm_prereq (void) { if (dm_libprereq()) return 1; return dm_drvprereq(TGT_MPATH); } static int dm_simplecmd (int task, const char *name, int no_flush, int need_sync) { int r = 0; int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE)); 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 if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, 0)) goto out; r = dm_task_run (dmt); out: dm_task_destroy (dmt); return r; } extern int dm_simplecmd_flush (int task, const char *name, int needsync) { return dm_simplecmd(task, name, 0, needsync); } extern int dm_simplecmd_noflush (int task, const char *name) { return dm_simplecmd(task, name, 1, 1); } extern int dm_addmap (int task, const char *target, struct multipath *mpp, int use_uuid, int ro) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; 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, mpp->params)) goto addout; if (ro) dm_task_set_ro(dmt); if (use_uuid && mpp->wwid){ prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1); if (!prefixed_uuid) { condlog(0, "cannot create prefixed uuid : %s\n", 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; dm_task_no_open_count(dmt); if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &conf->cookie, 0)) goto freeout; r = dm_task_run (dmt); freeout: if (prefixed_uuid) FREE(prefixed_uuid); addout: dm_task_destroy (dmt); return r; } static int _dm_addmap_create (struct multipath *mpp, int ro) { int r; r = dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, 1, ro); /* * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD. * Failing the second part leaves an empty map. Clean it up. */ if (!r && 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); } return r; } #define ADDMAP_RW 0 #define ADDMAP_RO 1 extern int dm_addmap_create (struct multipath *mpp) { return _dm_addmap_create(mpp, ADDMAP_RW); } extern int dm_addmap_create_ro (struct multipath *mpp) { return _dm_addmap_create(mpp, ADDMAP_RO); } extern int dm_addmap_reload (struct multipath *mpp) { return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, 0, ADDMAP_RW); } extern int dm_addmap_reload_ro (struct multipath *mpp) { return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, 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(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 (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } extern int dm_get_uuid(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) { if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN)) strcpy(uuid, uuidtmp + UUID_PREFIX_LEN); else strcpy(uuid, uuidtmp); } else uuid[0] = '\0'; r = 0; uuidout: dm_task_destroy(dmt); return r; } 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; } 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)) goto out; r = info.open_count; 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; r = info.open_count; 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; r = info.minor; out: dm_task_destroy(dmt); return r; } extern int _dm_flush_map (const char * mapname, int need_sync) { int r; if (!dm_map_present(mapname)) return 0; if (dm_type(mapname, TGT_MPATH) <= 0) return 0; /* nothing to do */ if (dm_remove_partmaps(mapname, need_sync)) return 1; if (dm_get_opencount(mapname)) { condlog(2, "%s: map in use", mapname); return 1; } r = dm_simplecmd_flush(DM_DEVICE_REMOVE, mapname, need_sync); if (r) { condlog(4, "multipath map %s removed", mapname); return 0; } 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_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\n", 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\n", 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\n"; else message = "fail_if_no_path\n"; return dm_message(mapname, message); } int dm_set_pg_timeout(char *mapname, int timeout_val) { char message[24]; if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24) return 1; 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\n", 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; int info; 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 { info = dm_type(names->name, TGT_MPATH); if (info <= 0) goto next; mpp = alloc_multipath(); if (!mpp) goto out; mpp->alias = STRDUP(names->name); if (!mpp->alias) goto out1; if (info > 0) { if (dm_get_map(names->name, &mpp->size, mpp->params)) goto out1; if (dm_get_status(names->name, mpp->status)) 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; } extern int dm_get_name(char *uuid, char *name) { vector vec; struct multipath *mpp; int i, rc = 0; vec = vector_alloc(); if (!vec) return 0; if (dm_get_maps(vec)) { goto out; } vector_foreach_slot(vec, mpp, i) { if (!strcmp(uuid, mpp->wwid)) { strcpy(name, mpp->alias); rc=1; break; } } out: vector_foreach_slot(vec, mpp, i) { free_multipath(mpp, KEEP_PATHS); } vector_free(vec); return rc; } int dm_geteventnr (char *name) { struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) 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.event_nr = 0; goto out; } if (!info.exists) { info.event_nr = 0; goto out; } out: dm_task_destroy(dmt); return info.event_nr; } 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; } int dm_remove_partmaps (const char * mapname, int need_sync) { 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 the multipath mapname and the part mapname start * the same */ !strncmp(names->name, mapname, strlen(mapname)) && /* * and the opencount is 0 for us to allow removal */ !dm_get_opencount(names->name) && /* * 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) ) { /* * then it's a kpartx generated partition. * remove it. */ condlog(4, "partition map %s removed", names->name); dm_simplecmd_flush(DM_DEVICE_REMOVE, names->name, need_sync); } next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } 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; } int dm_rename_partmaps (char * old, char * new) { struct dm_task *dmt; struct dm_names *names; unsigned next = 0; char buff[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(old, &dev_t[0], 32)) goto out; do { if ( /* * if devmap target is "linear" */ (dm_type(names->name, TGT_PART) > 0) && /* * and the multipath mapname and the part mapname start * the same */ !strncmp(names->name, old, strlen(old)) && /* * and we can fetch the map table from the kernel */ !dm_get_map(names->name, &size, &buff[0]) && /* * and the table maps over the multipath map */ strstr(buff, dev_t) ) { /* * then it's a kpartx generated partition. * Rename it. */ snprintf(buff, PARAMS_SIZE, "%s%s", new, names->name + strlen(old)); dm_rename(names->name, buff); condlog(4, "partition map %s renamed", names->name); } next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } int dm_rename (char * old, char * new) { int r = 0; struct dm_task *dmt; 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, &conf->cookie, 0)) goto out; if (!dm_task_run(dmt)) goto out; r = 1; out: dm_task_destroy(dmt); return r; } multipath-tools-0.4.9/libmultipath/devmapper.h000066400000000000000000000030071137574366600215450ustar00rootroot00000000000000#ifndef _DEVMAPPER_H #define _DEVMAPPER_H #include "structs.h" #define TGT_MPATH "multipath" #define TGT_PART "linear" void dm_init(void); int dm_prereq (void); int dm_simplecmd_flush (int, const char *, int); int dm_simplecmd_noflush (int, const char *); int dm_addmap_create (struct multipath *mpp); int dm_addmap_create_ro (struct multipath *mpp); int dm_addmap_reload (struct multipath *mpp); int dm_addmap_reload_ro (struct multipath *mpp); int dm_map_present (const char *); int dm_get_map(char *, unsigned long long *, char *); int dm_get_status(char *, char *); int dm_type(const char *, char *); int _dm_flush_map (const char *, int); #define dm_flush_map(mapname) _dm_flush_map(mapname, 1) #define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0) 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_set_pg_timeout(char *mapname, int timeout_val); 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_minor (char *name); char * dm_mapname(int major, int minor); int dm_remove_partmaps (const char * mapname, int need_sync); 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_get_name(char * uuid, char * name); #endif /* _DEVMAPPER_H */ multipath-tools-0.4.9/libmultipath/dict.c000066400000000000000000001304131137574366600205020ustar00rootroot00000000000000/* * 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 "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" /* * default block handlers */ static int polling_interval_handler(vector strvec) { char * buff; buff = VECTOR_SLOT(strvec, 1); conf->checkint = atoi(buff); conf->max_checkint = MAX_CHECKINT(conf->checkint); return 0; } static int def_fast_io_fail_handler(vector strvec) { char * buff; buff = set_value(strvec); if (strlen(buff) == 3 && !strcmp(buff, "off")) conf->fast_io_fail = -1; else if (sscanf(buff, "%d", &conf->fast_io_fail) != 1 || conf->fast_io_fail < -1) conf->fast_io_fail = 0; FREE(buff); return 0; } static int def_dev_loss_handler(vector strvec) { char * buff; buff = set_value(strvec); if (sscanf(buff, "%u", &conf->dev_loss) != 1) conf->dev_loss = 0; FREE(buff); return 0; } static int verbosity_handler(vector strvec) { char * buff; buff = VECTOR_SLOT(strvec, 1); conf->verbosity = atoi(buff); return 0; } static int udev_dir_handler(vector strvec) { conf->udev_dir = set_value(strvec); if (!conf->udev_dir) return 1; return 0; } static int multipath_dir_handler(vector strvec) { conf->multipath_dir = set_value(strvec); if (!conf->multipath_dir) return 1; return 0; } static int def_selector_handler(vector strvec) { conf->selector = set_value(strvec); if (!conf->selector) return 1; return 0; } static int def_pgpolicy_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; conf->pgpolicy = get_pgpolicy_id(buff); FREE(buff); return 0; } static int def_getuid_callout_handler(vector strvec) { conf->getuid = set_value(strvec); if (!conf->getuid) return 1; return 0; } static int def_prio_handler(vector strvec) { conf->prio_name = set_value(strvec); if (!conf->prio_name) return 1; return 0; } static int def_prio_args_handler(vector strvec) { conf->prio_args = set_value(strvec); if (!conf->prio_args) return 1; return 0; } static int def_features_handler(vector strvec) { conf->features = set_value(strvec); if (!conf->features) return 1; return 0; } static int def_path_checker_handler(vector strvec) { conf->checker_name = set_value(strvec); if (!conf->checker_name) return 1; return 0; } static int def_minio_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; conf->minio = atoi(buff); FREE(buff); return 0; } static 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; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 3 && !strcmp(buff, "max")) r = get_sys_max_fds(&conf->max_fds); else conf->max_fds = atoi(buff); FREE(buff); return r; } static int def_mode_handler(vector strvec) { mode_t mode; char *buff; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { conf->attribute_flags |= (1 << ATTR_MODE); conf->mode = mode; } FREE(buff); return 0; } static int def_uid_handler(vector strvec) { uid_t uid; 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) { conf->attribute_flags |= (1 << ATTR_UID); conf->uid = info.pw_uid; } else if (sscanf(buff, "%u", &uid) == 1){ conf->attribute_flags |= (1 << ATTR_UID); conf->uid = uid; } FREE(buff); return 0; } static int def_gid_handler(vector strvec) { gid_t gid; 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) { conf->attribute_flags |= (1 << ATTR_GID); conf->gid = info.pw_gid; } else if (sscanf(buff, "%u", &gid) == 1){ conf->attribute_flags |= (1 << ATTR_GID); conf->gid = gid; } FREE(buff); return 0; } static int def_weight_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 10 && !strcmp(buff, "priorities")) conf->rr_weight = RR_WEIGHT_PRIO; FREE(buff); return 0; } static int default_failback_handler(vector strvec) { char * buff; buff = set_value(strvec); if (strlen(buff) == 6 && !strcmp(buff, "manual")) conf->pgfailback = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) conf->pgfailback = -FAILBACK_IMMEDIATE; else conf->pgfailback = atoi(buff); FREE(buff); return 0; } static int def_no_path_retry_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || (strlen(buff) == 1 && !strcmp(buff, "0"))) conf->no_path_retry = NO_PATH_RETRY_FAIL; else if (strlen(buff) == 5 && !strcmp(buff, "queue")) conf->no_path_retry = NO_PATH_RETRY_QUEUE; else if ((conf->no_path_retry = atoi(buff)) < 1) conf->no_path_retry = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } static int def_queue_without_daemon(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strncmp(buff, "off", 3) || !strncmp(buff, "no", 2) || !strncmp(buff, "0", 1)) conf->queue_without_daemon = QUE_NO_DAEMON_OFF; else if (!strncmp(buff, "on", 2) || !strncmp(buff, "yes", 3) || !strncmp(buff, "1", 1)) conf->queue_without_daemon = QUE_NO_DAEMON_ON; else conf->queue_without_daemon = QUE_NO_DAEMON_UNDEF; free(buff); return 0; } static int def_pg_timeout_handler(vector strvec) { int pg_timeout; char * buff; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 4 && !strcmp(buff, "none")) conf->pg_timeout = -PGTIMEOUT_NONE; else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { if (pg_timeout == 0) conf->pg_timeout = -PGTIMEOUT_NONE; else conf->pg_timeout = pg_timeout; } else conf->pg_timeout = PGTIMEOUT_UNDEF; FREE(buff); return 0; } static int def_flush_on_last_del_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || (strlen(buff) == 1 && strcmp(buff, "0") == 0)) conf->flush_on_last_del = FLUSH_DISABLED; if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || (strlen(buff) == 1 && strcmp(buff, "1") == 0)) conf->flush_on_last_del = FLUSH_ENABLED; else conf->flush_on_last_del = FLUSH_UNDEF; FREE(buff); return 0; } static int names_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 2 && !strcmp(buff, "no")) || (strlen(buff) == 1 && !strcmp(buff, "0"))) conf->user_friendly_names = 0; else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || (strlen(buff) == 1 && !strcmp(buff, "1"))) conf->user_friendly_names = 1; FREE(buff); return 0; } /* * blacklist block handlers */ static int blacklist_handler(vector strvec) { conf->blist_devnode = vector_alloc(); conf->blist_wwid = vector_alloc(); conf->blist_device = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device) return 1; return 0; } static int blacklist_exceptions_handler(vector strvec) { conf->elist_devnode = vector_alloc(); conf->elist_wwid = vector_alloc(); conf->elist_device = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device) return 1; return 0; } static int ble_devnode_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return store_ble(conf->blist_devnode, buff, ORIGIN_CONFIG); } static int ble_except_devnode_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG); } static int ble_wwid_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return store_ble(conf->blist_wwid, buff, ORIGIN_CONFIG); } static int ble_except_wwid_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG); } 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); } static int ble_vendor_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return set_ble_device(conf->blist_device, buff, NULL, ORIGIN_CONFIG); } static int ble_except_vendor_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return set_ble_device(conf->elist_device, buff, NULL, ORIGIN_CONFIG); } static int ble_product_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return set_ble_device(conf->blist_device, NULL, buff, ORIGIN_CONFIG); } static int ble_except_product_handler(vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; return set_ble_device(conf->elist_device, NULL, buff, ORIGIN_CONFIG); } /* * 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; } static int vendor_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->vendor = set_value(strvec); if (!hwe->vendor) return 1; return 0; } static int product_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->product = set_value(strvec); if (!hwe->product) return 1; return 0; } static int bl_product_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->bl_product = set_value(strvec); if (!hwe->bl_product) return 1; return 0; } static int hw_fast_io_fail_handler(vector strvec) { char * buff; struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); buff = set_value(strvec); if (strlen(buff) == 3 && !strcmp(buff, "off")) hwe->fast_io_fail = -1; else if (sscanf(buff, "%d", &hwe->fast_io_fail) != 1 || hwe->fast_io_fail < -1) hwe->fast_io_fail = 0; FREE(buff); return 0; } static int hw_dev_loss_handler(vector strvec) { char * buff; struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); buff = set_value(strvec); if (sscanf(buff, "%u", &hwe->dev_loss) != 1) hwe->dev_loss = 0; FREE(buff); return 0; } static int hw_pgpolicy_handler(vector strvec) { char * buff; struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); buff = set_value(strvec); if (!buff) return 1; hwe->pgpolicy = get_pgpolicy_id(buff); FREE(buff); return 0; } static int hw_getuid_callout_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); hwe->getuid = set_value(strvec); if (!hwe->getuid) return 1; return 0; } static int hw_selector_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->selector = set_value(strvec); if (!hwe->selector) return 1; return 0; } static int hw_path_checker_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->checker_name = set_value(strvec); if (!hwe->checker_name) return 1; return 0; } static int hw_features_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->features = set_value(strvec); if (!hwe->features) return 1; return 0; } static int hw_handler_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->hwhandler = set_value(strvec); if (!hwe->hwhandler) return 1; return 0; } static int hw_prio_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->prio_name = set_value(strvec); if (!hwe->prio_name) return 1; return 0; } static int hw_prio_args_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); if (!hwe) return 1; hwe->prio_args = set_value(strvec); if (!hwe->prio_args) return 1; return 0; } static int hw_failback_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); char * buff; if (!hwe) return 1; buff = set_value(strvec); if (strlen(buff) == 6 && !strcmp(buff, "manual")) hwe->pgfailback = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) hwe->pgfailback = -FAILBACK_IMMEDIATE; else hwe->pgfailback = atoi(buff); FREE(buff); return 0; } static int hw_weight_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); char * buff; if (!hwe) return 1; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 10 && !strcmp(buff, "priorities")) hwe->rr_weight = RR_WEIGHT_PRIO; FREE(buff); return 0; } static int hw_no_path_retry_handler(vector strvec) { struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); char *buff; if (!hwe) return 1; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || (strlen(buff) == 1 && !strcmp(buff, "0"))) hwe->no_path_retry = NO_PATH_RETRY_FAIL; else if (strlen(buff) == 5 && !strcmp(buff, "queue")) hwe->no_path_retry = NO_PATH_RETRY_QUEUE; else if ((hwe->no_path_retry = atoi(buff)) < 1) hwe->no_path_retry = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } static int hw_minio_handler(vector strvec) { struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); char * buff; if (!hwe) return 1; buff = set_value(strvec); if (!buff) return 1; hwe->minio = atoi(buff); FREE(buff); return 0; } static int hw_pg_timeout_handler(vector strvec) { int pg_timeout; struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); char *buff; if (!hwe) return 1; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 4 && !strcmp(buff, "none")) hwe->pg_timeout = -PGTIMEOUT_NONE; else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { if (pg_timeout == 0) hwe->pg_timeout = -PGTIMEOUT_NONE; else hwe->pg_timeout = pg_timeout; } else hwe->pg_timeout = PGTIMEOUT_UNDEF; FREE(buff); return 0; } static int hw_flush_on_last_del_handler(vector strvec) { struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); char * buff; if (!hwe) return 1; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || (strlen(buff) == 1 && strcmp(buff, "0") == 0)) hwe->flush_on_last_del = FLUSH_DISABLED; if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || (strlen(buff) == 1 && strcmp(buff, "1") == 0)) hwe->flush_on_last_del = FLUSH_ENABLED; else hwe->flush_on_last_del = FLUSH_UNDEF; FREE(buff); return 0; } /* * multipaths block handlers */ static int multipaths_handler(vector strvec) { 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; } static int wwid_handler(vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; mpe->wwid = set_value(strvec); if (!mpe->wwid) return 1; return 0; } static int alias_handler(vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; mpe->alias = set_value(strvec); if (!mpe->alias) return 1; return 0; } static int mp_pgpolicy_handler(vector strvec) { char * buff; struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; mpe->pgpolicy = get_pgpolicy_id(buff); FREE(buff); return 0; } static int mp_selector_handler(vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; mpe->selector = set_value(strvec); if (!mpe->selector) return 1; return 0; } static int mp_failback_handler(vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); char * buff; if (!mpe) return 1; buff = set_value(strvec); if (strlen(buff) == 6 && !strcmp(buff, "manual")) mpe->pgfailback = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) mpe->pgfailback = -FAILBACK_IMMEDIATE; else mpe->pgfailback = atoi(buff); FREE(buff); return 0; } static int mp_mode_handler(vector strvec) { mode_t mode; struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); char *buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { mpe->attribute_flags |= (1 << ATTR_MODE); mpe->mode = mode; } FREE(buff); return 0; } static int mp_uid_handler(vector strvec) { uid_t uid; char *buff; char passwd_buf[1024]; struct passwd info, *found; struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { mpe->attribute_flags |= (1 << ATTR_UID); mpe->uid = info.pw_uid; } else if (sscanf(buff, "%u", &uid) == 1){ mpe->attribute_flags |= (1 << ATTR_UID); mpe->uid = uid; } FREE(buff); return 0; } static int mp_gid_handler(vector strvec) { gid_t gid; char *buff; char passwd_buf[1024]; struct passwd info, *found; struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { mpe->attribute_flags |= (1 << ATTR_GID); mpe->gid = info.pw_gid; } else if (sscanf(buff, "%u", &gid) == 1) { mpe->attribute_flags |= (1 << ATTR_GID); mpe->gid = gid; } FREE(buff); return 0; } static int mp_weight_handler(vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); char * buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 10 && !strcmp(buff, "priorities")) mpe->rr_weight = RR_WEIGHT_PRIO; FREE(buff); return 0; } static int mp_no_path_retry_handler(vector strvec) { struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); char *buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || (strlen(buff) == 1 && !strcmp(buff, "0"))) mpe->no_path_retry = NO_PATH_RETRY_FAIL; else if (strlen(buff) == 5 && !strcmp(buff, "queue")) mpe->no_path_retry = NO_PATH_RETRY_QUEUE; else if ((mpe->no_path_retry = atoi(buff)) < 1) mpe->no_path_retry = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } static int mp_minio_handler(vector strvec) { struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); char * buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; mpe->minio = atoi(buff); FREE(buff); return 0; } static int mp_pg_timeout_handler(vector strvec) { int pg_timeout; struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); char *buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 4 && !strcmp(buff, "none")) mpe->pg_timeout = -PGTIMEOUT_NONE; else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { if (pg_timeout == 0) mpe->pg_timeout = -PGTIMEOUT_NONE; else mpe->pg_timeout = pg_timeout; } else mpe->pg_timeout = PGTIMEOUT_UNDEF; FREE(buff); return 0; } static int mp_flush_on_last_del_handler(vector strvec) { struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); char * buff; if (!mpe) return 1; buff = set_value(strvec); if (!buff) return 1; if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || (strlen(buff) == 1 && strcmp(buff, "0") == 0)) mpe->flush_on_last_del = FLUSH_DISABLED; if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || (strlen(buff) == 1 && strcmp(buff, "1") == 0)) mpe->flush_on_last_del = FLUSH_ENABLED; else mpe->flush_on_last_del = FLUSH_UNDEF; FREE(buff); return 0; } /* * config file keywords printing */ static int snprint_mp_wwid (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; return snprintf(buff, len, "%s", mpe->wwid); } static int snprint_mp_alias (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->alias) return 0; if (conf->user_friendly_names && (strlen(mpe->alias) == strlen("mpath")) && !strcmp(mpe->alias, "mpath")) return 0; return snprintf(buff, len, "%s", mpe->alias); } static int snprint_mp_path_grouping_policy (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; char str[POLICY_NAME_SIZE]; if (!mpe->pgpolicy) return 0; get_pgpolicy_name(str, POLICY_NAME_SIZE, mpe->pgpolicy); return snprintf(buff, len, "%s", str); } static int snprint_mp_selector (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->selector) return 0; return snprintf(buff, len, "%s", mpe->selector); } static int snprint_mp_failback (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->pgfailback) return 0; switch(mpe->pgfailback) { case FAILBACK_UNDEF: break; case -FAILBACK_MANUAL: return snprintf(buff, len, "manual"); case -FAILBACK_IMMEDIATE: return snprintf(buff, len, "immediate"); default: return snprintf(buff, len, "%i", mpe->pgfailback); } return 0; } static int snprint_mp_mode(char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if ((mpe->attribute_flags & (1 << ATTR_MODE)) == 0) return 0; return snprintf(buff, len, "0%o", mpe->mode); } static int snprint_mp_uid(char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if ((mpe->attribute_flags & (1 << ATTR_UID)) == 0) return 0; return snprintf(buff, len, "0%o", mpe->uid); } static int snprint_mp_gid(char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if ((mpe->attribute_flags & (1 << ATTR_GID)) == 0) return 0; return snprintf(buff, len, "0%o", mpe->gid); } static int snprint_mp_rr_weight (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->rr_weight) return 0; if (mpe->rr_weight == RR_WEIGHT_PRIO) return snprintf(buff, len, "priorities"); return 0; } static int snprint_mp_no_path_retry (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->no_path_retry) return 0; switch(mpe->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; 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", mpe->no_path_retry); } return 0; } static int snprint_mp_rr_min_io (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; if (!mpe->minio) return 0; return snprintf(buff, len, "%u", mpe->minio); } static int snprint_mp_pg_timeout (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; switch (mpe->pg_timeout) { case PGTIMEOUT_UNDEF: break; case -PGTIMEOUT_NONE: return snprintf(buff, len, "none"); default: return snprintf(buff, len, "%i", mpe->pg_timeout); } return 0; } static int snprint_mp_flush_on_last_del (char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; switch (mpe->flush_on_last_del) { case FLUSH_DISABLED: return snprintf(buff, len, "no"); case FLUSH_ENABLED: return snprintf(buff, len, "yes"); } return 0; } static int snprint_hw_fast_io_fail(char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->fast_io_fail) return 0; if (hwe->fast_io_fail == -1) return snprintf(buff, len, "off"); return snprintf(buff, len, "%d", hwe->fast_io_fail); } static int snprint_hw_dev_loss(char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->dev_loss) return 0; return snprintf(buff, len, "%u", hwe->dev_loss); } static int snprint_hw_vendor (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->vendor) return 0; return snprintf(buff, len, "\"%s\"", hwe->vendor); } static int snprint_hw_product (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->product) return 0; return snprintf(buff, len, "\"%s\"", hwe->product); } static int snprint_hw_bl_product (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->bl_product) return 0; return snprintf(buff, len, "\"%s\"", hwe->bl_product); } static int snprint_hw_getuid_callout (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->getuid) return 0; if (conf->getuid && strlen(hwe->getuid) == strlen(conf->getuid) && !strcmp(hwe->getuid, conf->getuid)) return 0; return snprintf(buff, len, "\"%s\"", hwe->getuid); } static int snprint_hw_prio (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->prio_name || (strlen(hwe->prio_name) == 0)) return 0; if (conf->prio_name && !strcmp(hwe->prio_name, conf->prio_name)) return 0; return snprintf(buff, len, "%s", hwe->prio_name); } static int snprint_hw_prio_args (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->prio_args || (strlen(hwe->prio_args) == 0)) return 0; if (conf->prio_args && !strcmp(hwe->prio_args, conf->prio_args)) return 0; return snprintf(buff, len, "%s", hwe->prio_args); } static int snprint_hw_features (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->features) return 0; if (conf->features && strlen(hwe->features) == strlen(conf->features) && !strcmp(hwe->features, conf->features)) return 0; return snprintf(buff, len, "\"%s\"", hwe->features); } static int snprint_hw_hardware_handler (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->hwhandler) return 0; if (conf->hwhandler && strlen(hwe->hwhandler) == strlen(conf->hwhandler) && !strcmp(hwe->hwhandler, conf->hwhandler)) return 0; return snprintf(buff, len, "\"%s\"", hwe->hwhandler); } static int snprint_hw_selector (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->selector) return 0; if (conf->selector && strlen(hwe->selector) == strlen(conf->selector) && !strcmp(hwe->selector, conf->selector)) return 0; return snprintf(buff, len, "%s", hwe->selector); } static int snprint_hw_path_grouping_policy (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; char str[POLICY_NAME_SIZE]; if (!hwe->pgpolicy) return 0; if (conf->pgpolicy && hwe->pgpolicy == conf->pgpolicy) return 0; get_pgpolicy_name(str, POLICY_NAME_SIZE, hwe->pgpolicy); return snprintf(buff, len, "%s", str); } static int snprint_hw_failback (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->pgfailback) return 0; if (conf->pgfailback && hwe->pgfailback == conf->pgfailback) return 0; switch(hwe->pgfailback) { case FAILBACK_UNDEF: break; case -FAILBACK_MANUAL: return snprintf(buff, len, "manual"); case -FAILBACK_IMMEDIATE: return snprintf(buff, len, "immediate"); default: return snprintf(buff, len, "%i", hwe->pgfailback); } return 0; } static int snprint_hw_rr_weight (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->rr_weight) return 0; if (conf->rr_weight && hwe->rr_weight == conf->rr_weight) return 0; if (hwe->rr_weight == RR_WEIGHT_PRIO) return snprintf(buff, len, "priorities"); return 0; } static int snprint_hw_no_path_retry (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->no_path_retry) return 0; if (hwe->no_path_retry == conf->no_path_retry) return 0; switch(hwe->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; 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", hwe->no_path_retry); } return 0; } static int snprint_hw_rr_min_io (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->minio) return 0; if (hwe->minio == conf->minio) return 0; return snprintf(buff, len, "%u", hwe->minio); } static int snprint_hw_pg_timeout (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->pg_timeout) return 0; if (hwe->pg_timeout == conf->pg_timeout) return 0; switch (hwe->pg_timeout) { case PGTIMEOUT_UNDEF: break; case -PGTIMEOUT_NONE: return snprintf(buff, len, "none"); default: return snprintf(buff, len, "%i", hwe->pg_timeout); } return 0; } static int snprint_hw_flush_on_last_del (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; switch (hwe->flush_on_last_del) { case FLUSH_DISABLED: return snprintf(buff, len, "no"); case FLUSH_ENABLED: return snprintf(buff, len, "yes"); } return 0; } static int snprint_hw_path_checker (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; if (!hwe->checker_name) return 0; if (conf->checker_name && !strcmp(hwe->checker_name, conf->checker_name)) return 0; return snprintf(buff, len, "%s", hwe->checker_name); } static int snprint_def_polling_interval (char * buff, int len, void * data) { if (conf->checkint == DEFAULT_CHECKINT) return 0; return snprintf(buff, len, "%i", conf->checkint); } static int snprint_def_fast_io_fail(char * buff, int len, void * data) { if (!conf->fast_io_fail) return 0; if (conf->fast_io_fail == -1) return snprintf(buff, len, "off"); return snprintf(buff, len, "%d", conf->fast_io_fail); } static int snprint_def_dev_loss(char * buff, int len, void * data) { if (!conf->dev_loss) return 0; return snprintf(buff, len, "%u", conf->dev_loss); } static int snprint_def_verbosity (char * buff, int len, void * data) { if (conf->checkint == DEFAULT_VERBOSITY) return 0; return snprintf(buff, len, "%i", conf->verbosity); } static int snprint_def_udev_dir (char * buff, int len, void * data) { if (!conf->udev_dir) return 0; if (strlen(DEFAULT_UDEVDIR) == strlen(conf->udev_dir) && !strcmp(conf->udev_dir, DEFAULT_UDEVDIR)) return 0; return snprintf(buff, len, "\"%s\"", conf->udev_dir); } static int snprint_def_multipath_dir (char * buff, int len, void * data) { if (!conf->udev_dir) return 0; if (strlen(DEFAULT_MULTIPATHDIR) == strlen(conf->multipath_dir) && !strcmp(conf->multipath_dir, DEFAULT_MULTIPATHDIR)) return 0; return snprintf(buff, len, "\"%s\"", conf->multipath_dir); } static int snprint_def_selector (char * buff, int len, void * data) { if (!conf->selector) return 0; if (strlen(conf->selector) == strlen(DEFAULT_SELECTOR) && !strcmp(conf->selector, DEFAULT_SELECTOR)) return 0; return snprintf(buff, len, "%s", conf->selector); } static int snprint_def_path_grouping_policy (char * buff, int len, void * data) { char str[POLICY_NAME_SIZE]; if (!conf->pgpolicy) return 0; if (conf->pgpolicy == DEFAULT_PGPOLICY) return 0; get_pgpolicy_name(str, POLICY_NAME_SIZE, conf->pgpolicy); return snprintf(buff, len, "%s", str); } static int snprint_def_getuid_callout (char * buff, int len, void * data) { if (!conf->getuid) return 0; if (strlen(conf->getuid) == strlen(DEFAULT_GETUID) && !strcmp(conf->getuid, DEFAULT_GETUID)) return 0; return snprintf(buff, len, "\"%s\"", conf->getuid); } static int snprint_def_prio (char * buff, int len, void * data) { if (!conf->prio_name) return 0; if (strlen(conf->prio_name) == strlen(DEFAULT_PRIO) && !strcmp(conf->prio_name, DEFAULT_PRIO)) return 0; return snprintf(buff, len, "%s", conf->prio_name); } static int snprint_def_prio_args (char * buff, int len, void * data) { if (!conf->prio_args) return 0; if (strlen(conf->prio_args) == strlen(DEFAULT_PRIO_ARGS) && !strcmp(conf->prio_args, DEFAULT_PRIO_ARGS)) return 0; return snprintf(buff, len, "%s", conf->prio_args); } static int snprint_def_features (char * buff, int len, void * data) { if (!conf->features) return 0; if (strlen(conf->features) == strlen(DEFAULT_FEATURES) && !strcmp(conf->features, DEFAULT_FEATURES)) return 0; return snprintf(buff, len, "\"%s\"", conf->features); } static int snprint_def_path_checker (char * buff, int len, void * data) { if (!conf->checker_name) return 0; if (strlen(conf->checker_name) == strlen(DEFAULT_CHECKER) && !strcmp(conf->checker_name, DEFAULT_CHECKER)) return 0; return snprintf(buff, len, "%s", conf->checker_name); } static int snprint_def_failback (char * buff, int len, void * data) { if (!conf->pgfailback) return 0; if (conf->pgfailback == DEFAULT_FAILBACK) return 0; switch(conf->pgfailback) { case FAILBACK_UNDEF: break; case -FAILBACK_MANUAL: return snprintf(buff, len, "manual"); case -FAILBACK_IMMEDIATE: return snprintf(buff, len, "immediate"); default: return snprintf(buff, len, "%i", conf->pgfailback); } return 0; } static int snprint_def_rr_min_io (char * buff, int len, void * data) { if (!conf->minio) return 0; if (conf->minio == DEFAULT_MINIO) return 0; return snprintf(buff, len, "%u", conf->minio); } static int snprint_max_fds (char * buff, int len, void * data) { if (!conf->max_fds) return 0; return snprintf(buff, len, "%d", conf->max_fds); } static int snprint_def_mode(char * buff, int len, void * data) { if ((conf->attribute_flags & (1 << ATTR_MODE)) == 0) return 0; return snprintf(buff, len, "0%o", conf->mode); } static int snprint_def_uid(char * buff, int len, void * data) { if ((conf->attribute_flags & (1 << ATTR_UID)) == 0) return 0; return snprintf(buff, len, "0%o", conf->uid); } static int snprint_def_gid(char * buff, int len, void * data) { if ((conf->attribute_flags & (1 << ATTR_GID)) == 0) return 0; return snprintf(buff, len, "0%o", conf->gid); } static int snprint_def_rr_weight (char * buff, int len, void * data) { if (!conf->rr_weight) return 0; if (conf->rr_weight == DEFAULT_RR_WEIGHT) return 0; if (conf->rr_weight == RR_WEIGHT_PRIO) return snprintf(buff, len, "priorities"); return 0; } static int snprint_def_no_path_retry (char * buff, int len, void * data) { if (conf->no_path_retry == DEFAULT_NO_PATH_RETRY) return 0; switch(conf->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; 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", conf->no_path_retry); } return 0; } 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"); } return 0; } static int snprint_def_pg_timeout (char * buff, int len, void * data) { if (conf->pg_timeout == DEFAULT_PGTIMEOUT) return 0; switch (conf->pg_timeout) { case PGTIMEOUT_UNDEF: break; case -PGTIMEOUT_NONE: return snprintf(buff, len, "none"); default: return snprintf(buff, len, "%i", conf->pg_timeout); } return 0; } static int snprint_def_flush_on_last_del (char * buff, int len, void * data) { switch (conf->flush_on_last_del) { case FLUSH_DISABLED: return snprintf(buff, len, "no"); case FLUSH_ENABLED: return snprintf(buff, len, "yes"); } return 0; } static int snprint_def_user_friendly_names (char * buff, int len, void * data) { if (conf->user_friendly_names == DEFAULT_USER_FRIENDLY_NAMES) return 0; if (!conf->user_friendly_names) return snprintf(buff, len, "no"); return snprintf(buff, len, "yes"); } 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 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); } #define __deprecated void init_keywords(void) { install_keyword_root("defaults", NULL); install_keyword("verbosity", &verbosity_handler, &snprint_def_verbosity); install_keyword("polling_interval", &polling_interval_handler, &snprint_def_polling_interval); install_keyword("udev_dir", &udev_dir_handler, &snprint_def_udev_dir); install_keyword("multipath_dir", &multipath_dir_handler, &snprint_def_multipath_dir); install_keyword("selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy); install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout); install_keyword("prio", &def_prio_handler, &snprint_def_prio); 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_path_checker_handler, &snprint_def_path_checker); install_keyword("checker", &def_path_checker_handler, &snprint_def_path_checker); install_keyword("failback", &default_failback_handler, &snprint_def_failback); install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); install_keyword("rr_weight", &def_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, &snprint_def_queue_without_daemon); install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout); install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del); install_keyword("user_friendly_names", &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); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL); install_keyword_root("blacklist", &blacklist_handler); install_keyword("devnode", &ble_devnode_handler, &snprint_ble_simple); install_keyword("wwid", &ble_wwid_handler, &snprint_ble_simple); install_keyword("device", &ble_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_product_handler, &snprint_bled_product); install_sublevel_end(); install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); install_keyword("devnode", &ble_except_devnode_handler, &snprint_ble_simple); install_keyword("wwid", &ble_except_wwid_handler, &snprint_ble_simple); install_keyword("device", &ble_except_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_except_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("device", &device_handler, NULL); install_sublevel(); install_keyword("vendor", &vendor_handler, &snprint_hw_vendor); install_keyword("product", &product_handler, &snprint_hw_product); install_keyword("product_blacklist", &bl_product_handler, &snprint_hw_bl_product); install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_path_grouping_policy); install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout); install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector); install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker); install_keyword("checker", &hw_path_checker_handler, &snprint_hw_path_checker); install_keyword("features", &hw_features_handler, &snprint_hw_features); install_keyword("hardware_handler", &hw_handler_handler, &snprint_hw_hardware_handler); install_keyword("prio", &hw_prio_handler, &snprint_hw_prio); install_keyword("prio_args", &hw_prio_args_handler, &snprint_hw_prio_args); install_keyword("failback", &hw_failback_handler, &snprint_hw_failback); install_keyword("rr_weight", &hw_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_rr_min_io); install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout); 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_sublevel_end(); install_keyword_root("multipaths", &multipaths_handler); install_keyword("multipath", &multipath_handler, NULL); install_sublevel(); install_keyword("wwid", &wwid_handler, &snprint_mp_wwid); install_keyword("alias", &alias_handler, &snprint_mp_alias); install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_path_grouping_policy); install_keyword("path_selector", &mp_selector_handler, &snprint_mp_selector); install_keyword("failback", &mp_failback_handler, &snprint_mp_failback); install_keyword("rr_weight", &mp_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_rr_min_io); install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout); install_keyword("flush_on_last_del", &mp_flush_on_last_del_handler, &snprint_mp_flush_on_last_del); 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_sublevel_end(); } multipath-tools-0.4.9/libmultipath/dict.h000066400000000000000000000001771137574366600205120ustar00rootroot00000000000000#ifndef _DICT_H #define _DICT_H #ifndef _VECTOR_H #include "vector.h" #endif void init_keywords(void); #endif /* _DICT_H */ multipath-tools-0.4.9/libmultipath/discovery.c000066400000000000000000000447611137574366600216000ustar00rootroot00000000000000/* * 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 "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" struct path * store_pathinfo (vector pathvec, vector hwtable, char * devname, int flag) { struct path * pp; pp = alloc_path(); if (!pp) return NULL; if(safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); goto out; } if (pathinfo(pp, hwtable, flag)) goto out; if (store_path(pathvec, pp)) goto out; return pp; out: free_path(pp); return NULL; } static int path_discover (vector pathvec, struct config * conf, char * devname, int flag) { char path[FILE_NAME_SIZE]; struct path * pp; if (!devname) return 0; if (filter_devnode(conf->blist_devnode, conf->elist_devnode, devname) > 0) return 0; if(safe_sprintf(path, "%s/block/%s/device", sysfs_path, devname)) { condlog(0, "path too small"); return 1; } if (strncmp(devname,"cciss",5) && !filepresent(path)) { condlog(4, "path %s not present", path); return 0; } pp = find_path_by_dev(pathvec, devname); if (!pp) { pp = store_pathinfo(pathvec, conf->hwtable, devname, flag); return (pp ? 0 : 1); } return pathinfo(pp, conf->hwtable, flag); } int path_discovery (vector pathvec, struct config * conf, int flag) { DIR *blkdir; struct dirent *blkdev; struct stat statbuf; char devpath[PATH_MAX]; char *devptr; int r = 0; if (!(blkdir = opendir("/sys/block"))) return 1; strcpy(devpath,"/sys/block"); while ((blkdev = readdir(blkdir)) != NULL) { if ((strcmp(blkdev->d_name,".") == 0) || (strcmp(blkdev->d_name,"..") == 0)) continue; devptr = devpath + 10; *devptr = '\0'; strcat(devptr,"/"); strcat(devptr,blkdev->d_name); if (stat(devpath, &statbuf) < 0) continue; if (S_ISDIR(statbuf.st_mode) == 0) continue; condlog(4, "Discover device %s", devpath); r += path_discover(pathvec, conf, blkdev->d_name, flag); } closedir(blkdir); condlog(4, "Discovery status %d", r); return r; } #define declare_sysfs_get_str(fname) \ extern int \ sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \ { \ char *attr; \ \ attr = sysfs_attr_get_value(dev->devpath, #fname); \ if (!attr) \ return 1; \ if (strlcpy(buff, attr, len) != strlen(attr)) \ return 2; \ strchop(buff); \ return 0; \ } declare_sysfs_get_str(devtype); declare_sysfs_get_str(cutype); declare_sysfs_get_str(vendor); declare_sysfs_get_str(model); declare_sysfs_get_str(rev); declare_sysfs_get_str(state); int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len) { char *attr; attr = sysfs_attr_get_value(dev->devpath, "dev"); if (!attr) { condlog(3, "%s: no 'dev' attribute in sysfs", dev->kernel); return 1; } if (strlcpy(buff, attr, len) != strlen(attr)) { condlog(3, "%s: overflow in 'dev' attribute", dev->kernel); return 2; } return 0; } int sysfs_get_size (struct sysfs_device * dev, unsigned long long * size) { char *attr; int r; attr = sysfs_attr_get_value(dev->devpath, "size"); if (!attr) return 1; r = sscanf(attr, "%llu\n", size); if (r != 1) return 1; return 0; } int sysfs_get_fc_nodename (struct sysfs_device * dev, char * node, unsigned int host, unsigned int channel, unsigned int target) { char attr_path[SYSFS_PATH_SIZE], *attr; if (safe_sprintf(attr_path, "/class/fc_transport/target%i:%i:%i", host, channel, target)) { condlog(0, "attr_path too small"); return 1; } attr = sysfs_attr_get_value(attr_path, "node_name"); if (attr) { strlcpy(node, attr, strlen(attr)); return 0; } return 1; } int sysfs_set_scsi_tmo (struct multipath *mpp) { char attr_path[SYSFS_PATH_SIZE]; struct path *pp; int i; char value[11]; if (!mpp->dev_loss && !mpp->fast_io_fail) return 0; vector_foreach_slot(mpp->paths, pp, i) { if (safe_snprintf(attr_path, SYSFS_PATH_SIZE, "/class/fc_remote_ports/rport-%d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id)) { condlog(0, "attr_path '/class/fc_remote_ports/rport-%d:%d-%d' too large", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id); return 1; } if (mpp->dev_loss){ snprintf(value, 11, "%u", mpp->dev_loss); if (sysfs_attr_set_value(attr_path, "dev_loss_tmo", value)) return 1; } if (mpp->fast_io_fail){ if (mpp->fast_io_fail == -1) sprintf(value, "off"); else snprintf(value, 11, "%u", mpp->fast_io_fail); if (sysfs_attr_set_value(attr_path, "fast_io_fail_tmo", value)) return 1; } } return 0; } static int opennode (char * dev, int mode) { char devpath[FILE_NAME_SIZE], *ptr; if (safe_sprintf(devpath, "%s/%s", conf->udev_dir, dev)) { condlog(0, "devpath too small"); return -1; } /* * Translate '!' into '/' */ ptr = devpath; while ((ptr = strchr(ptr, '!'))) { *ptr = '/'; ptr++; } return open(devpath, mode); } extern int devt2devname (char *devname, char *devt) { FILE *fd; unsigned int tmpmaj, tmpmin, major, minor; char dev[FILE_NAME_SIZE]; char block_path[FILE_NAME_SIZE]; struct stat statbuf; memset(block_path, 0, FILE_NAME_SIZE); if (sscanf(devt, "%u:%u", &major, &minor) != 2) { condlog(0, "Invalid device number %s", devt); return 1; } 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, FILE_NAME_SIZE, "/sys/block/%s", dev) >= FILE_NAME_SIZE) { condlog(0, "device name %s is too long\n", dev); fclose(fd); return 1; } break; } } fclose(fd); if (strncmp(block_path,"/sys/block", 10)) return 1; if (stat(block_path, &statbuf) < 0) { condlog(0, "No sysfs entry for %s\n", block_path); return 1; } if (S_ISDIR(statbuf.st_mode) == 0) { condlog(0, "sysfs entry %s is not a directory\n", block_path); return 1; } basenamecpy(block_path, devname); 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)); 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; } static int get_inq (char * dev, char * vendor, char * product, char * rev, int fd) { unsigned char buff[MX_ALLOC_LEN + 1] = {0}; int len; if (fd < 0) return 1; if (0 != do_inq(fd, 0, 0, 0, buff, MX_ALLOC_LEN)) return 1; /* Check peripheral qualifier */ if ((buff[0] >> 5) != 0) { int pqual = (buff[0] >> 5); switch (pqual) { case 1: condlog(3, "%s: INQUIRY failed, LU not connected", dev); break; case 3: condlog(3, "%s: INQUIRY failed, LU not supported", dev); break; default: condlog(3, "%s: INQUIRY failed, Invalid PQ %x", dev, pqual); break; } return 1; } len = buff[4] + 4; if (len < 8) { condlog(3, "%s: INQUIRY response too short (len %d)", dev, len); return 1; } len -= 8; memset(vendor, 0x0, 8); memcpy(vendor, buff + 8, len > 8 ? 8 : len); vendor[8] = '\0'; strchop(vendor); if (len <= 8) return 0; len -= 8; memset(product, 0x0, 16); memcpy(product, buff + 16, len > 16 ? 16 : len); product[16] = '\0'; strchop(product); if (len <= 16) return 0; len -= 16; memset(rev, 0x0, 4); memcpy(rev, buff + 32, 4); rev[4] = '\0'; strchop(rev); return 0; } static int scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE)) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE)) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE)) 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 */ basenamecpy(parent->devpath, attr_path); 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); 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_fc_nodename(parent, pp->tgt_node_name, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id)) { condlog(3, "%s: tgt_node_name = %s", pp->dev, pp->tgt_node_name); } return 0; } static int ccw_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; char attr_buff[FILE_NAME_SIZE]; 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)) 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 */ basenamecpy(parent->devpath, attr_path); 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, struct sysfs_device * dev) { char attr_path[FILE_NAME_SIZE]; /* * host / bus / target / lun */ basenamecpy(dev->devpath, attr_path); pp->sg_id.lun = 0; pp->sg_id.channel = 0; sscanf(attr_path, "cciss!c%id%i", &pp->sg_id.host_no, &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 common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev) { char *attr; attr = sysfs_attr_get_value(dev->devpath, "dev"); if (!attr) { condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev); return 1; } strlcpy(pp->dev_t, attr, BLK_DEV_SIZE); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); if (sysfs_get_size(dev, &pp->size)) return 1; condlog(3, "%s: size = %llu", pp->dev, pp->size); return 0; } struct sysfs_device *sysfs_device_from_path(struct path *pp) { char sysdev[FILE_NAME_SIZE]; strlcpy(sysdev,"/block/", FILE_NAME_SIZE); strlcat(sysdev,pp->dev, FILE_NAME_SIZE); return sysfs_device_get(sysdev); } int path_offline (struct path * pp) { struct sysfs_device * parent; char buff[SCSI_STATE_SIZE]; pp->sysdev = sysfs_device_from_path(pp); if (!pp->sysdev) { condlog(1, "%s: failed to get sysfs information", pp->dev); return PATH_WILD; } parent = sysfs_device_get_parent(pp->sysdev); if (!parent) parent = pp->sysdev; if (parent && !strncmp(parent->kernel, "block",5)) parent = sysfs_device_get_parent(parent); if (!parent) { condlog(1, "%s: failed to get parent", pp->dev); return PATH_WILD; } if (sysfs_get_state(parent, buff, SCSI_STATE_SIZE)) return PATH_WILD; condlog(3, "%s: state = %s", pp->dev, buff); if (!strncmp(buff, "offline", 7)) { pp->offline = 1; return PATH_DOWN; } pp->offline = 0; if (!strncmp(buff, "blocked", 7)) return PATH_PENDING; else if (!strncmp(buff, "running", 7)) return PATH_UP; return PATH_DOWN; } extern int sysfs_pathinfo(struct path * pp) { struct sysfs_device *parent; pp->sysdev = sysfs_device_from_path(pp); if (!pp->sysdev) { condlog(1, "%s: failed to get sysfs information", pp->dev); return 1; } if (common_sysfs_pathinfo(pp, pp->sysdev)) return 1; parent = sysfs_device_get_parent(pp->sysdev); if (!parent) parent = pp->sysdev; if (parent && !strncmp(parent->kernel, "block",5)) parent = sysfs_device_get_parent(parent); if (!parent) { condlog(1, "%s: failed to get parent", pp->dev); return 1; } if (!strncmp(pp->dev,"cciss",5)) strcpy(parent->subsystem,"cciss"); condlog(3, "%s: subsystem = %s", pp->dev, parent->subsystem); if (!strncmp(parent->subsystem, "scsi",4)) pp->bus = SYSFS_BUS_SCSI; if (!strncmp(parent->subsystem, "ccw",3)) pp->bus = SYSFS_BUS_CCW; if (!strncmp(parent->subsystem,"cciss",5)) pp->bus = SYSFS_BUS_CCISS; if (pp->bus == SYSFS_BUS_UNDEF) return 0; else if (pp->bus == SYSFS_BUS_SCSI) { if (scsi_sysfs_pathinfo(pp, parent)) return 1; } else if (pp->bus == SYSFS_BUS_CCW) { if (ccw_sysfs_pathinfo(pp, parent)) return 1; } else if (pp->bus == SYSFS_BUS_CCISS) { if (cciss_sysfs_pathinfo(pp, pp->sysdev)) return 1; } return 0; } static int scsi_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; } static int cciss_ioctl_pathinfo (struct path * pp, int mask) { int ret; if (mask & DI_SYSFS) { ret = get_inq(pp->dev, pp->vendor_id, pp->product_id, pp->rev, pp->fd); if (ret) return ret; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: revision = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); } 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) pathinfo(pp, conf->hwtable, DI_SYSFS); 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)) { condlog(3, "%s: checker init failed", pp->dev); return PATH_UNCHECKED; } } state = path_offline(pp); if (state != PATH_UP) { condlog(3, "%s: path inaccessible", pp->dev); return state; } if (daemon) checker_set_async(c); state = checker_check(c); condlog(3, "%s: state = %i", pp->dev, state); if (state == PATH_DOWN && 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; if (!pp->prio) { select_prio(pp); if (!pp->prio) { condlog(3, "%s: no prio selected", pp->dev); return 1; } } pp->priority = prio_getprio(pp->prio, pp); if (pp->priority < 0) { condlog(3, "%s: %s prio error", pp->dev, prio_name(pp->prio)); pp->priority = PRIO_UNDEF; return 1; } condlog(3, "%s: %s prio = %u", pp->dev, prio_name(pp->prio), pp->priority); return 0; } static int get_uid (struct path * pp) { char buff[CALLOUT_MAX_SIZE]; int i; if (!pp->getuid) select_getuid(pp); if (apply_format(pp->getuid, &buff[0], pp)) { condlog(0, "error formatting uid callout command"); memset(pp->wwid, 0, WWID_SIZE); } else if (execute_program(buff, pp->wwid, WWID_SIZE)) { condlog(3, "error calling out %s", buff); memset(pp->wwid, 0, WWID_SIZE); return 1; } /* Strip any trailing blanks */ i = WWID_SIZE - 1; while (i > 0 && pp->wwid[i] == ' ') { pp->wwid[i] = '\0'; i--; } condlog(3, "%s: uid = %s (callout)", pp->dev ,pp->wwid); return 0; } extern int pathinfo (struct path *pp, vector hwtable, int mask) { condlog(3, "%s: mask = 0x%x", pp->dev, mask); /* * fetch info available in sysfs */ if (mask & DI_SYSFS && sysfs_pathinfo(pp)) return 1; /* * fetch info not available through sysfs */ if (pp->fd < 0) pp->fd = opennode(pp->dev, O_RDONLY); if (pp->fd < 0) { condlog(4, "Couldn't open node for %s: %s", pp->dev, strerror(errno)); goto blank; } if (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) { pp->state = get_state(pp, 0); if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) goto blank; } /* * Retrieve path priority, even for PATH_DOWN paths if it has never * been successfully obtained before. */ if (mask & DI_PRIO && (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF)) get_prio(pp); if (mask & DI_WWID && !strlen(pp->wwid)) get_uid(pp); return 0; blank: /* * Recoverable error, for example faulty or offline path */ memset(pp->wwid, 0, WWID_SIZE); pp->state = PATH_DOWN; return 0; } multipath-tools-0.4.9/libmultipath/discovery.h000066400000000000000000000025451137574366600215770ustar00rootroot00000000000000#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 300000 #endif /* * exerpt from sg_err.h */ #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len); int path_discovery (vector pathvec, struct config * conf, int flag); int do_tur (char *); int devt2devname (char *, char *); int path_offline (struct path *); int get_state (struct path * pp, int daemon); int pathinfo (struct path *, vector hwtable, int mask); struct path * store_pathinfo (vector pathvec, vector hwtable, char * devname, int flag); int sysfs_set_scsi_tmo (struct multipath *mpp); /* * discovery bitmask */ enum discovery_mode { __DI_SYSFS, __DI_SERIAL, __DI_CHECKER, __DI_PRIO, __DI_WWID }; #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_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \ DI_WWID) #endif /* DISCOVERY_H */ multipath-tools-0.4.9/libmultipath/dmparser.c000066400000000000000000000205141137574366600213740ustar00rootroot00000000000000/* * 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" #define WORD_SIZE 64 static int merge_words (char ** dst, char * word, int space) { char * p; int len; len = strlen(*dst) + strlen(word) + space; *dst = REALLOC(*dst, len + 1); if (!*dst) 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) { int i, j; int shift, freechar; int minio; char * p; struct pathgroup * pgp; struct path * pp; minio = mp->minio; p = mp->params; freechar = sizeof(mp->params); shift = snprintf(p, freechar, "%s %s %i %i", mp->features, mp->hwhandler, VECTOR_SIZE(mp->pg), mp->bestpg); if (shift >= freechar) { fprintf(stderr, "mp->params too small\n"); 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) { fprintf(stderr, "mp->params too small\n"); 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; shift = snprintf(p, freechar, " %s %d", pp->dev_t, tmp_minio); if (shift >= freechar) { fprintf(stderr, "mp->params too small\n"); return 1; } p += shift; freechar -= shift; } } if (freechar < 1) { fprintf(stderr, "mp->params too small\n"); return 1; } snprintf(p, 1, "\n"); 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; struct path * pp; struct pathgroup * pgp; p = params; /* * features */ p += get_word(p, &mpp->features); if (!mpp->features) return 1; num_features = atoi(mpp->features); 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); } /* * 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 && !mpp->pg) { mpp->pg = vector_alloc(); if (!mpp->pg) return 1; } else 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)) { FREE(word); 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 (!mpp->waiter && 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; /* * 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.4.9/libmultipath/dmparser.h000066400000000000000000000002261137574366600213770ustar00rootroot00000000000000int assemble_map (struct multipath *); int disassemble_map (vector, char *, struct multipath *); int disassemble_status (char *, struct multipath *); multipath-tools-0.4.9/libmultipath/hwtable.c000066400000000000000000000757271137574366600212250ustar00rootroot00000000000000#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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .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 ", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DEFAULT_CHECKER, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "DEC", .product = "HSG80", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = HP_SW, .prio_name = PRIO_HP_SW, .prio_args = NULL, }, { .vendor = "HP", .product = "A6189A", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = DEFAULT_MINIO, .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.*", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .selector = DEFAULT_SELECTOR, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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|HSV300|HSV4[05]0", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 alua", .selector = DEFAULT_SELECTOR, .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.*", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 12, .minio = DEFAULT_MINIO, .checker_name = CCISS_TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * DDN controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "DDN", .product = "SAN DataDirector", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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", .getuid = "/lib/udev/scsi_id --page=pre-spc3-83 --whitelisted --device=/dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "DGC", .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 emc", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .minio = DEFAULT_MINIO, .checker_name = EMC_CLARIION, .prio_name = PRIO_EMC, .prio_args = NULL, }, { .vendor = "EMC", .product = "Invista", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 5, .minio = DEFAULT_MINIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Fujitsu controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "FSC", .product = "CentricStor", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "EMC", .product = "Invista", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = FAILOVER, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 5, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Hitachi controller family * * Maintainer : Matthias Rudolph * Mail : matthias.rudolph@hds.com */ { .vendor = "(HITACHI|HP)", .product = "OPEN-.*", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "HITACHI", .product = "DF.*", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM FAStT 1722-600 */ .vendor = "IBM", .product = "1722-600", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4100 */ .vendor = "IBM", .product = "1724", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS3200 / DS3300 / DS3400 */ .vendor = "IBM", .product = "1726", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 300, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4400 / DS4500 / FAStT700 */ .vendor = "IBM", .product = "1742", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "IBM", .product = "1745|1746", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4700 */ .vendor = "IBM", .product = "1814", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4800 */ .vendor = "IBM", .product = "1815", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS5000 */ .vendor = "IBM", .product = "1818", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM Netfinity Fibre Channel RAID Controller Unit */ .vendor = "IBM", .product = "3526", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* IBM DS4200 / FAStT200 */ .vendor = "IBM", .product = "3542", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", .product = "2105800", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", .product = "2105F20", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_SERIAL, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM DS6000 */ .vendor = "IBM", .product = "1750500", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, { /* IBM DS8000 */ .vendor = "IBM", .product = "2107900", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM SAN Volume Controller */ .vendor = "IBM", .product = "2145", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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.*", .getuid = "/sbin/dasdinfo -u -b %n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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.*", .getuid = "/sbin/dasdinfo -u -b %n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM IPR */ .vendor = "IBM", .product = "IPR.*", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = "1 alua", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = PRIO_ALUA, .prio_args = NULL, }, /* * IBM Power Virtual SCSI Devices * * Maintainer : Brian King, IBM * Mail : brking@linux.vnet.ibm.com */ { /* AIX VDASD */ .vendor = "AIX", .product = "VDASD", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* IBM 3303 NVDISK */ .vendor = "IBM", .product = "3303 NVDISK", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = FAILOVER, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = (300 / DEFAULT_CHECKINT), .minio = DEFAULT_MINIO, .checker_name = TUR, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { /* DELL MD3000 */ .vendor = "DELL", .product = "MD3000", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD3000i */ .vendor = "DELL", .product = "MD3000i", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD32xx */ .vendor = "DELL", .product = "MD32xx", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { /* DELL MD32xxi */ .vendor = "DELL", .product = "MD32xxi", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* * NETAPP controller family * * Maintainer : Dave Wysochanski * Mail : davidw@netapp.com */ { .vendor = "NETAPP", .product = "LUN.*", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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_NETAPP, .prio_args = NULL, }, /* * NEXENTA/COMSTAR controller family * * Maintainer : Yacine Kheddache * Mail : yacine@alyseo.com */ { .vendor = "NEXENTA", .product = "COMSTAR", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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.*", .getuid = DEFAULT_GETUID, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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_NETAPP, .prio_args = NULL, }, /* * Pillar Data controller family * * Maintainer : Srinivasan Ramani * Mail : sramani@pillardata.com */ { .vendor = "Pillar", .product = "Axiom.*", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, { .vendor = "SGI", .product = "TP9[45]00", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "SGI", .product = "IS.*", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* * STK arrays * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@opensvc.com */ { .vendor = "STK", .product = "OPENstorage D280", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .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)", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, /* * Pivot3 RAIGE * * Maintainer : Bart Brooks, Pivot3 * Mail : bartb@pivot3.com */ { .vendor = "PIVOT3", .product = "RAIGE VOLUME", .getuid = "/lib/udev/scsi_id --page=0x80 --whitelisted --device=/dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .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", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* SUN/LSI 2510, 2540, 2530, 2540 */ { .vendor = "SUN", .product = "LCSM100_[IEFS]", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* LSI Generic rdac storage */ { .vendor = "(LSI|ENGENIO)", .product = "INF-01-00", .getuid = DEFAULT_GETUID, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = 15, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, { .vendor = "STK", .product = "FLEXLINE 380", .bl_product = "Universal Xport", .getuid = DEFAULT_GETUID, .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, .prio_name = PRIO_RDAC, .prio_args = NULL, }, /* * EOL */ { .vendor = NULL, .product = NULL, .getuid = NULL, .features = NULL, .hwhandler = NULL, .selector = NULL, .pgpolicy = 0, .pgfailback = 0, .rr_weight = 0, .no_path_retry = 0, .minio = 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.4.9/libmultipath/hwtable.h000066400000000000000000000001471137574366600212120ustar00rootroot00000000000000#ifndef _HWTABLE_H #define _HWTABLE_H int setup_default_hwtable (vector hw); #endif /* _HWTABLE_H */ multipath-tools-0.4.9/libmultipath/list.h000066400000000000000000000176371137574366600205530ustar00rootroot00000000000000/* * 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 /** * 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(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->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); } /** * 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); 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.4.9/libmultipath/lock.c000066400000000000000000000004641137574366600205110ustar00rootroot00000000000000#include #include #include "lock.h" #include void block_signal (int signum, sigset_t *old) { sigset_t set; sigemptyset(&set); sigaddset(&set, signum); pthread_sigmask(SIG_BLOCK, &set, old); } void cleanup_lock (void * data) { unlock ((*(struct mutex_lock *)data)); } multipath-tools-0.4.9/libmultipath/lock.h000066400000000000000000000021271137574366600205140ustar00rootroot00000000000000#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); void block_signal(int signum, sigset_t *old); #endif /* _LOCK_H */ multipath-tools-0.4.9/libmultipath/log.c000066400000000000000000000075761137574366600203550ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Jun'ichi Nomura, NEC */ #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; } 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; strcpy((void *)&msg->str, buff); 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.4.9/libmultipath/log.h000066400000000000000000000013011137574366600203360ustar00rootroot00000000000000#ifndef LOG_H #define LOG_H #define DEFAULT_AREA_SIZE 8192 #define MAX_MSG_SIZE 128 #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; }; 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); 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.4.9/libmultipath/log_pthread.c000066400000000000000000000041021137574366600220420ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include "log_pthread.h" #include "log.h" #include "lock.h" void log_safe (int prio, const char * fmt, va_list ap) { sigset_t old; block_signal(SIGUSR1, &old); block_signal(SIGHUP, NULL); 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); pthread_sigmask(SIG_SETMASK, &old, NULL); } 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) { 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); pthread_mutex_unlock(logev_lock); flush_logqueue(); } return NULL; } void log_thread_start (pthread_attr_t *attr) { logdbg(stderr,"enter log_thread_start\n"); logq_lock = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); logev_lock = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); logev_cond = (pthread_cond_t *) malloc(sizeof(pthread_cond_t)); 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); } pthread_create(&log_thr, attr, log_thread, NULL); return; } void log_thread_stop (void) { logdbg(stderr,"enter log_thread_stop\n"); pthread_mutex_lock(logq_lock); pthread_cancel(log_thr); pthread_mutex_unlock(logq_lock); flush_logqueue(); pthread_mutex_destroy(logq_lock); pthread_mutex_destroy(logev_lock); pthread_cond_destroy(logev_cond); free(logq_lock); logq_lock = NULL; free(logev_lock); logev_lock = NULL; free(logev_cond); logev_cond = NULL; free_logarea(); } multipath-tools-0.4.9/libmultipath/log_pthread.h000066400000000000000000000005141137574366600220520ustar00rootroot00000000000000#ifndef _LOG_PTHREAD_H #define _LOG_PTHREAD_H #include pthread_t log_thr; pthread_mutex_t *logq_lock; pthread_mutex_t *logev_lock; pthread_cond_t *logev_cond; void log_safe(int prio, const char * fmt, va_list ap); void log_thread_start(pthread_attr_t *attr); void log_thread_stop(void); #endif /* _LOG_PTHREAD_H */ multipath-tools-0.4.9/libmultipath/memory.c000066400000000000000000000253551137574366600210770ustar00rootroot00000000000000/* * 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.4.9/libmultipath/memory.h000066400000000000000000000044571137574366600211040ustar00rootroot00000000000000/* * 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.4.9/libmultipath/parser.c000066400000000000000000000216601137574366600210560ustar00rootroot00000000000000/* * 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 "parser.h" #include "memory.h" /* local vars */ static int sublevel = 0; vector keywords = NULL; vector *keywords_addr = NULL; 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 *)) { 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; vector_set_slot(keywords, keyword); return 0; } int install_keyword_root(char *string, int (*handler) (vector)) { int r = keyword_alloc(keywords, string, handler, NULL); 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 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); } 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; } int read_line(char *buf, int size) { int ch; int count = 0; while ((ch = fgetc(stream)) != EOF && (int) ch != '\n' && (int) ch != '\r') { if (count < size) buf[count] = (int) ch; else break; count++; } return (ch == EOF) ? 0 : 1; } vector read_value_block(void) { char *buf; int i; char *str = NULL; char *dup; vector vec = NULL; vector elements = vector_alloc(); if (!elements) return NULL; buf = (char *) MALLOC(MAXBUF); if (!buf) return NULL; while (read_line(buf, MAXBUF)) { vec = alloc_strvec(buf); if (vec) { str = VECTOR_SLOT(vec, 0); if (!strcmp(str, EOB)) { free_strvec(vec); break; } if (VECTOR_SIZE(vec)) for (i = 0; i < VECTOR_SIZE(vec); i++) { str = VECTOR_SLOT(vec, i); dup = (char *) MALLOC(strlen(str) + 1); if (!dup) goto out; memcpy(dup, str, strlen(str)); if (!vector_alloc_slot(elements)) { free_strvec(vec); goto out1; } vector_set_slot(elements, dup); } free_strvec(vec); } memset(buf, 0, MAXBUF); } FREE(buf); return elements; out1: FREE(dup); out: FREE(buf); return NULL; } int alloc_value_block(vector strvec, void (*alloc_func) (vector)) { char *buf; char *str = NULL; vector vec = NULL; buf = (char *) MALLOC(MAXBUF); if (!buf) return 1; while (read_line(buf, MAXBUF)) { vec = alloc_strvec(buf); if (vec) { str = VECTOR_SLOT(vec, 0); if (!strcmp(str, EOB)) { free_strvec(vec); break; } if (VECTOR_SIZE(vec)) (*alloc_func) (vec); free_strvec(vec); } memset(buf, 0, MAXBUF); } FREE(buf); return 0; } void * set_value(vector strvec) { char *str = VECTOR_SLOT(strvec, 1); int size = strlen(str); int i = 0; int len = 0; char *alloc = NULL; char *tmp; if (*str == '"') { for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); len += strlen(str); if (!alloc) alloc = (char *) MALLOC(sizeof (char *) * (len + 1)); else { alloc = REALLOC(alloc, sizeof (char *) * (len + 1)); tmp = VECTOR_SLOT(strvec, i-1); if (alloc && *str != '"' && *tmp != '"') strncat(alloc, " ", 1); } if (alloc && i != VECTOR_SIZE(strvec)-1) strncat(alloc, str, strlen(str)); } } else { alloc = MALLOC(sizeof (char *) * (size + 1)); if (alloc) memcpy(alloc, str, size); } return alloc; } /* non-recursive configuration stream handler */ static int kw_level = 0; int process_stream(vector keywords) { int i; int r = 0; struct keyword *keyword; char *str; char *buf; vector strvec; buf = MALLOC(MAXBUF); if (!buf) return 1; while (read_line(buf, MAXBUF)) { strvec = alloc_strvec(buf); memset(buf,0, MAXBUF); if (!strvec) continue; str = VECTOR_SLOT(strvec, 0); if (!strcmp(str, EOB) && kw_level > 0) { free_strvec(strvec); break; } for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (!strcmp(keyword->string, str)) { if (keyword->handler) r += (*keyword->handler) (strvec); if (keyword->sub) { kw_level++; r += process_stream(keyword->sub); kw_level--; } break; } } free_strvec(strvec); } FREE(buf); return r; } /* Data initialization */ int init_data(char *conf_file, void (*init_keywords) (void)) { int r; if (!keywords) keywords = vector_alloc(); if (!keywords) return 1; stream = fopen(conf_file, "r"); if (!stream) { syslog(LOG_WARNING, "Configuration file open problem"); return 1; } /* Init Keywords structure */ (*init_keywords) (); /* Dump configuration * vector_dump(keywords); dump_keywords(keywords, 0); */ /* Stream handling */ r = process_stream(keywords); fclose(stream); //free_keywords(keywords); return r; } multipath-tools-0.4.9/libmultipath/parser.h000066400000000000000000000053171137574366600210640ustar00rootroot00000000000000/* * 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; }; /* global var exported */ FILE *stream; /* 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 *)); 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 *)); extern void dump_keywords(vector keydump, int level); extern void free_keywords(vector keywords); extern vector alloc_strvec(char *string); extern int read_line(char *buf, int size); extern vector read_value_block(void); extern int alloc_value_block(vector strvec, void (*alloc_func) (vector)); extern void *set_value(vector strvec); extern int process_stream(vector keywords); extern int init_data(char *conf_file, void (*init_keywords) (void)); 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.4.9/libmultipath/pgpolicies.c000066400000000000000000000133671137574366600217250ustar00rootroot00000000000000/* * 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" 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 -1; } 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); } /* * 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); 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); 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; } 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.4.9/libmultipath/pgpolicies.h000066400000000000000000000011121137574366600217130ustar00rootroot00000000000000#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.4.9/libmultipath/print.c000066400000000000000000000734761137574366600207320ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #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" #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, 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->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 path * pp = first_path(mpp); if (!pp) return 0; return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } 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->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 (!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 (!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->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) { 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"); default: return snprintf(buff, len, "undef"); } } static int snprint_dm_path_state (char * buff, size_t len, struct path * pp) { 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->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->priority); } 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) { int avg_priority = 0; /* * 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); if (pgp->enabled_paths) avg_priority = pgp->priority / pgp->enabled_paths; return snprint_int(buff, len, avg_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_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}, {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) { 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); 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) { 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); 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) { char buff[MAX_LINE_LEN * MAX_LINES] = {}; memset(&buff[0], 0, MAX_LINE_LEN * MAX_LINES); snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES, mpp, verbosity); printf("%s", 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); 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); if (fwd > len) return len; fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp); 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); 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_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, "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; } 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; } 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) 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) 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); 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); 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) { if (mpp->size && mpp->params) printf("0 %llu %s %s\n", mpp->size, TGT_MPATH, mpp->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.4.9/libmultipath/print.h000066400000000000000000000042101137574366600207130ustar00rootroot00000000000000#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 snprint_multipath (char *, int, char *, struct multipath *); 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); 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); 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.4.9/libmultipath/prio.c000066400000000000000000000035601137574366600205320ustar00rootroot00000000000000#include #include #include #include #include "debug.h" #include "prio.h" #include "config.h" static LIST_HEAD(prioritizers); int init_prio (void) { if (!add_prio(DEFAULT_PRIO)) return 1; return 0; } static struct prio * alloc_prio (void) { return MALLOC(sizeof(struct prio)); } void free_prio (struct prio * p) { 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) { list_del(&prio_loop->node); free_prio(prio_loop); } } struct prio * prio_lookup (char * name) { struct prio * p; 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]; void * handle; struct prio * p; char *errstr; p = alloc_prio(); if (!p) return NULL; snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so", conf->multipath_dir, name); condlog(3, "loading %s prioritizer", libname); handle = dlopen(libname, RTLD_NOW); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!handle) goto out; p->getprio = (int (*)(struct path *, char *)) dlsym(handle, "getprio"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!p->getprio) goto out; snprintf(p->name, PRIO_NAME_LEN, "%s", name); 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); } char * prio_name (struct prio * p) { return p->name; } char * prio_args (struct prio * p) { return p->args; } multipath-tools-0.4.9/libmultipath/prio.h000066400000000000000000000022341137574366600205340ustar00rootroot00000000000000#ifndef _PRIO_H #define _PRIO_H /* * knowing about path struct gives flexibility to prioritizers */ #include "checkers.h" #include "vector.h" #include "structs.h" #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_NETAPP "netapp" #define PRIO_RANDOM "random" #define PRIO_RDAC "rdac" #define PRIO_DATACORE "datacore" /* * 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 { struct list_head node; char name[PRIO_NAME_LEN]; char args[PRIO_ARGS_LEN]; int (*getprio)(struct path *, char *); }; 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 *); char * prio_name (struct prio *); char * prio_args (struct prio *); int prio_set_args (struct prio *, char *); #endif /* _PRIO_H */ multipath-tools-0.4.9/libmultipath/prioritizers/000077500000000000000000000000001137574366600221565ustar00rootroot00000000000000multipath-tools-0.4.9/libmultipath/prioritizers/Makefile000066400000000000000000000011711137574366600236160ustar00rootroot00000000000000# Makefile # # Copyright (C) 2007 Christophe Varoqui, # include ../../Makefile.inc LIBS = \ libpriorandom.so \ libprioconst.so \ libpriohp_sw.so \ libprioemc.so \ libpriordac.so \ libprioalua.so \ libprionetapp.so \ libpriodatacore.so \ libpriohds.so CFLAGS += -I.. all: $(LIBS) libprioalua.so: alua.o alua_rtpg.o $(CC) $(SHARED_FLAGS) -o $@ $^ libprio%.so: %.o $(CC) $(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.4.9/libmultipath/prioritizers/alua.c000066400000000000000000000044121137574366600232450ustar00rootroot00000000000000/* * (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 "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 int get_alua_info(int fd) { char * aas_string[] = { [AAS_OPTIMIZED] = "active/optimized", [AAS_NON_OPTIMIZED] = "active/non-optimized", [AAS_STANDBY] = "standby", [AAS_UNAVAILABLE] = "unavailable", [AAS_TRANSITIONING] = "transitioning between states", }; int rc; int tpg; int aas; 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; aas = (rc & 0x0f); condlog(3, "aas = [%s]", (aas < 4) ? aas_string[aas] : "invalid/reserved"); 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_STANDBY: rc = 1; break; default: rc = 0; } if (priopath) 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: couln't get asymmetric access state", pp->dev); break; case ALUA_PRIO_TPGS_FAILED: condlog(3, "%s: couln't get supported alua states", pp->dev); break; } } return rc; } multipath-tools-0.4.9/libmultipath/prioritizers/alua.h000066400000000000000000000001731137574366600232520ustar00rootroot00000000000000#ifndef _ALUA_H #define _ALUA_H #include "alua_rtpg.h" #define PRIO_ALUA "alua" int prio_alua(struct path * pp); #endif multipath-tools-0.4.9/libmultipath/prioritizers/alua_rtpg.c000066400000000000000000000147161137574366600243110ustar00rootroot00000000000000/* * (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 "alua_rtpg.h" #define SENSE_BUFF_LEN 32 #define DEF_TIMEOUT 300000 /* * 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 = 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[128]; struct vpd83_data * vpd83; struct vpd83_dscr * dscr; int rc; memset(buf, 0, sizeof(buf)); rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf)); if (!rc) { vpd83 = (struct vpd83_data *) buf; rc = -RTPG_NO_TPG_IDENTIFIER; FOR_EACH_VPD83_DSCR(vpd83, dscr) { if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf)) break; 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"); } } 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 = 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) return rc; 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.4.9/libmultipath/prioritizers/alua_rtpg.h000066400000000000000000000013461137574366600243110ustar00rootroot00000000000000/* * (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.4.9/libmultipath/prioritizers/alua_spc3.h000066400000000000000000000237531137574366600242130ustar00rootroot00000000000000/* * (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_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; /* xxxx.... = reserved */ /* ....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.4.9/libmultipath/prioritizers/const.c000066400000000000000000000001421137574366600234450ustar00rootroot00000000000000#include #include int getprio (struct path * pp, char * args) { return 1; } multipath-tools-0.4.9/libmultipath/prioritizers/datacore.c000066400000000000000000000053431137574366600241110ustar00rootroot00000000000000/* * (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 #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, 9, "%.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.4.9/libmultipath/prioritizers/emc.c000066400000000000000000000041261137574366600230710ustar00rootroot00000000000000#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[256]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sb), 0}; struct sg_io_hdr io_hdr; int ret = 0; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); 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 = 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"); } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { pp_emc_log(0, "path not available for normal operations"); } /* 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. */ ret = (sense_buffer[5] == sense_buffer[8]) ? 1 : 0; out: return(ret); } int getprio (struct path * pp, char * args) { return emc_clariion_prio(pp->dev, pp->fd); } multipath-tools-0.4.9/libmultipath/prioritizers/hds.c000066400000000000000000000126661137574366600231130ustar00rootroot00000000000000/* * (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 #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[8]; 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)); 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 = 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.4.9/libmultipath/prioritizers/hp_sw.c000066400000000000000000000053761137574366600234550ustar00rootroot00000000000000/* * 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 #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 = 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.4.9/libmultipath/prioritizers/netapp.c000066400000000000000000000136511137574366600236170ustar00rootroot00000000000000/* * 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 #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define DEFAULT_PRIOVAL 10 #define RESULTS_MAX 256 #define SG_TIMEOUT 30000 #define pp_netapp_log(prio, fmt, args...) \ condlog(prio, "%s: netapp 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)); 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 = SG_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_netapp_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_netapp_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_netapp_log(0, "GVA return wrong format "); pp_netapp_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 = SG_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_netapp_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_netapp_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_netapp_log(0,"proxy info page in unknown format - "); pp_netapp_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 netapp_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_netapp_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; } } 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 netapp_prio(pp->dev, pp->fd); } multipath-tools-0.4.9/libmultipath/prioritizers/random.c000066400000000000000000000004221137574366600236000ustar00rootroot00000000000000#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.4.9/libmultipath/prioritizers/rdac.c000066400000000000000000000041051137574366600232330ustar00rootroot00000000000000#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[256]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, sizeof(sb), 0}; struct sg_io_hdr io_hdr; int ret = 0; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); 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 = 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; } out: return(ret); } int getprio (struct path * pp, char * args) { return rdac_prio(pp->dev, pp->fd); } multipath-tools-0.4.9/libmultipath/propsel.c000066400000000000000000000331001137574366600212360ustar00rootroot00000000000000/* * 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" pgpolicyfn *pgpolicies[] = { NULL, one_path_per_group, one_group, group_by_serial, group_by_prio, group_by_node_name }; extern int select_mode (struct multipath *mp) { if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_MODE))) { mp->attribute_flags |= (1 << ATTR_MODE); mp->mode = mp->mpe->mode; condlog(3, "mode = 0%o (LUN setting)", mp->mode); } else if (conf->attribute_flags & (1 << ATTR_MODE)) { mp->attribute_flags |= (1 << ATTR_MODE); mp->mode = conf->mode; condlog(3, "mode = 0%o (config file default)", mp->mode); } else mp->attribute_flags &= ~(1 << ATTR_MODE); return 0; } extern int select_uid (struct multipath *mp) { if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_UID))) { mp->attribute_flags |= (1 << ATTR_UID); mp->uid = mp->mpe->uid; condlog(3, "uid = %u (LUN setting)", mp->uid); } else if (conf->attribute_flags & (1 << ATTR_UID)) { mp->attribute_flags |= (1 << ATTR_UID); mp->uid = conf->uid; condlog(3, "uid = %u (config file default)", mp->uid); } else mp->attribute_flags &= ~(1 << ATTR_UID); return 0; } extern int select_gid (struct multipath *mp) { if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_GID))) { mp->attribute_flags |= (1 << ATTR_GID); mp->gid = mp->mpe->gid; condlog(3, "gid = %u (LUN setting)", mp->gid); } else if (conf->attribute_flags & (1 << ATTR_GID)) { mp->attribute_flags |= (1 << ATTR_GID); mp->gid = conf->gid; condlog(3, "gid = %u (config file default)", mp->gid); } else mp->attribute_flags &= ~(1 << ATTR_GID); 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) { if (mp->mpe && mp->mpe->rr_weight) { mp->rr_weight = mp->mpe->rr_weight; condlog(3, "%s: rr_weight = %i (LUN setting)", mp->alias, mp->rr_weight); return 0; } if (mp->hwe && mp->hwe->rr_weight) { mp->rr_weight = mp->hwe->rr_weight; condlog(3, "%s: rr_weight = %i (controller setting)", mp->alias, mp->rr_weight); return 0; } if (conf->rr_weight) { mp->rr_weight = conf->rr_weight; condlog(3, "%s: rr_weight = %i (config file default)", mp->alias, mp->rr_weight); return 0; } mp->rr_weight = RR_WEIGHT_NONE; condlog(3, "%s: rr_weight = %i (internal default)", mp->alias, mp->rr_weight); return 0; } extern int select_pgfailback (struct multipath * mp) { if (mp->mpe && mp->mpe->pgfailback != FAILBACK_UNDEF) { mp->pgfailback = mp->mpe->pgfailback; condlog(3, "%s: pgfailback = %i (LUN setting)", mp->alias, mp->pgfailback); return 0; } if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) { mp->pgfailback = mp->hwe->pgfailback; condlog(3, "%s: pgfailback = %i (controller setting)", mp->alias, mp->pgfailback); return 0; } if (conf->pgfailback != FAILBACK_UNDEF) { mp->pgfailback = conf->pgfailback; condlog(3, "%s: pgfailback = %i (config file default)", mp->alias, mp->pgfailback); return 0; } mp->pgfailback = DEFAULT_FAILBACK; condlog(3, "%s: pgfailover = %i (internal default)", mp->alias, mp->pgfailback); return 0; } extern int select_pgpolicy (struct multipath * mp) { char pgpolicy_name[POLICY_NAME_SIZE]; if (conf->pgpolicy_flag > 0) { mp->pgpolicy = conf->pgpolicy_flag; mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: pgpolicy = %s (cmd line flag)", mp->alias, pgpolicy_name); return 0; } if (mp->mpe && mp->mpe->pgpolicy > 0) { mp->pgpolicy = mp->mpe->pgpolicy; mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: pgpolicy = %s (LUN setting)", mp->alias, pgpolicy_name); return 0; } if (mp->hwe && mp->hwe->pgpolicy > 0) { mp->pgpolicy = mp->hwe->pgpolicy; mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: pgpolicy = %s (controller setting)", mp->alias, pgpolicy_name); return 0; } if (conf->pgpolicy > 0) { mp->pgpolicy = conf->pgpolicy; mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: pgpolicy = %s (config file default)", mp->alias, pgpolicy_name); return 0; } mp->pgpolicy = DEFAULT_PGPOLICY; mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: pgpolicy = %s (internal default)", mp->alias, pgpolicy_name); return 0; } extern int select_selector (struct multipath * mp) { if (mp->mpe && mp->mpe->selector) { mp->selector = mp->mpe->selector; condlog(3, "%s: selector = %s (LUN setting)", mp->alias, mp->selector); return 0; } if (mp->hwe && mp->hwe->selector) { mp->selector = mp->hwe->selector; condlog(3, "%s: selector = %s (controller setting)", mp->alias, mp->selector); return 0; } if (conf->selector) { mp->selector = conf->selector; condlog(3, "%s: selector = %s (config file default)", mp->alias, mp->selector); return 0; } mp->selector = set_default(DEFAULT_SELECTOR); condlog(3, "%s: selector = %s (internal default)", mp->alias, mp->selector); return 0; } extern int select_alias (struct multipath * mp) { if (mp->mpe && mp->mpe->alias) mp->alias = mp->mpe->alias; else { mp->alias = NULL; if (conf->user_friendly_names) mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file); if (mp->alias == NULL){ char *alias; if ((alias = MALLOC(WWID_SIZE)) != NULL){ if (dm_get_name(mp->wwid, alias) == 1) mp->alias = alias; else FREE(alias); } } if (mp->alias == NULL) mp->alias = mp->wwid; } return 0; } extern int select_features (struct multipath * mp) { if (mp->hwe && mp->hwe->features) { mp->features = mp->hwe->features; condlog(3, "%s: features = %s (controller setting)", mp->alias, mp->features); return 0; } if (conf->features) { mp->features = conf->features; condlog(3, "%s: features = %s (config file default)", mp->alias, mp->features); return 0; } mp->features = set_default(DEFAULT_FEATURES); condlog(3, "%s: features = %s (internal default)", mp->alias, mp->features); return 0; } extern int select_hwhandler (struct multipath * mp) { if (mp->hwe && mp->hwe->hwhandler) { mp->hwhandler = mp->hwe->hwhandler; condlog(3, "%s: hwhandler = %s (controller setting)", mp->alias, mp->hwhandler); return 0; } if (conf->hwhandler) { mp->hwhandler = conf->hwhandler; condlog(3, "%s: hwhandler = %s (config file default)", mp->alias, mp->hwhandler); return 0; } mp->hwhandler = set_default(DEFAULT_HWHANDLER); condlog(3, "%s: hwhandler = %s (internal default)", mp->alias, mp->hwhandler); return 0; } extern int select_checker(struct path *pp) { struct checker * c = &pp->checker; if (pp->hwe && pp->hwe->checker_name) { checker_get(c, pp->hwe->checker_name); condlog(3, "%s: path checker = %s (controller setting)", pp->dev, checker_name(c)); return 0; } if (conf->checker_name) { checker_get(c, conf->checker_name); condlog(3, "%s: path checker = %s (config file default)", pp->dev, checker_name(c)); return 0; } checker_get(c, DEFAULT_CHECKER); condlog(3, "%s: path checker = %s (internal default)", pp->dev, checker_name(c)); return 0; } extern int select_getuid (struct path * pp) { if (pp->hwe && pp->hwe->getuid) { pp->getuid = pp->hwe->getuid; condlog(3, "%s: getuid = %s (controller setting)", pp->dev, pp->getuid); return 0; } if (conf->getuid) { pp->getuid = conf->getuid; condlog(3, "%s: getuid = %s (config file default)", pp->dev, pp->getuid); return 0; } pp->getuid = STRDUP(DEFAULT_GETUID); condlog(3, "%s: getuid = %s (internal default)", pp->dev, pp->getuid); return 0; } extern int select_prio (struct path * pp) { if (pp->hwe && pp->hwe->prio_name) { pp->prio = prio_lookup(pp->hwe->prio_name); prio_set_args(pp->prio, pp->hwe->prio_args); condlog(3, "%s: prio = %s (controller setting)", pp->dev, pp->hwe->prio_name); condlog(3, "%s: prio args = %s (controller setting)", pp->dev, pp->hwe->prio_args); return 0; } if (conf->prio_name) { pp->prio = prio_lookup(conf->prio_name); prio_set_args(pp->prio, conf->prio_args); condlog(3, "%s: prio = %s (config file default)", pp->dev, conf->prio_name); condlog(3, "%s: prio args = %s (config file default)", pp->dev, conf->prio_args); return 0; } pp->prio = prio_lookup(DEFAULT_PRIO); prio_set_args(pp->prio, DEFAULT_PRIO_ARGS); condlog(3, "%s: prio = %s (internal default)", pp->dev, DEFAULT_PRIO); condlog(3, "%s: prio = %s (internal default)", pp->dev, DEFAULT_PRIO_ARGS); return 0; } extern int select_no_path_retry(struct multipath *mp) { 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; } if (mp->mpe && mp->mpe->no_path_retry != NO_PATH_RETRY_UNDEF) { mp->no_path_retry = mp->mpe->no_path_retry; condlog(3, "%s: no_path_retry = %i (multipath setting)", mp->alias, mp->no_path_retry); return 0; } if (mp->hwe && mp->hwe->no_path_retry != NO_PATH_RETRY_UNDEF) { mp->no_path_retry = mp->hwe->no_path_retry; condlog(3, "%s: no_path_retry = %i (controller setting)", mp->alias, mp->no_path_retry); return 0; } if (conf->no_path_retry != NO_PATH_RETRY_UNDEF) { mp->no_path_retry = conf->no_path_retry; condlog(3, "%s: no_path_retry = %i (config file default)", mp->alias, mp->no_path_retry); return 0; } mp->no_path_retry = NO_PATH_RETRY_UNDEF; condlog(3, "%s: no_path_retry = NONE (internal default)", mp->alias); return 0; } extern int select_minio (struct multipath * mp) { if (mp->mpe && mp->mpe->minio) { mp->minio = mp->mpe->minio; condlog(3, "%s: minio = %i (LUN setting)", mp->alias, mp->minio); return 0; } if (mp->hwe && mp->hwe->minio) { mp->minio = mp->hwe->minio; condlog(3, "%s: minio = %i (controller setting)", mp->alias, mp->minio); return 0; } if (conf->minio) { mp->minio = conf->minio; condlog(3, "%s: minio = %i (config file default)", mp->alias, mp->minio); return 0; } mp->minio = DEFAULT_MINIO; condlog(3, "%s: minio = %i (internal default)", mp->alias, mp->minio); return 0; } extern int select_pg_timeout(struct multipath *mp) { if (mp->mpe && mp->mpe->pg_timeout != PGTIMEOUT_UNDEF) { mp->pg_timeout = mp->mpe->pg_timeout; if (mp->pg_timeout > 0) condlog(3, "%s: pg_timeout = %d (multipath setting)", mp->alias, mp->pg_timeout); else condlog(3, "%s: pg_timeout = NONE (multipath setting)", mp->alias); return 0; } if (mp->hwe && mp->hwe->pg_timeout != PGTIMEOUT_UNDEF) { mp->pg_timeout = mp->hwe->pg_timeout; if (mp->pg_timeout > 0) condlog(3, "%s: pg_timeout = %d (controller setting)", mp->alias, mp->pg_timeout); else condlog(3, "%s: pg_timeout = NONE (controller setting)", mp->alias); return 0; } if (conf->pg_timeout != PGTIMEOUT_UNDEF) { mp->pg_timeout = conf->pg_timeout; if (mp->pg_timeout > 0) condlog(3, "%s: pg_timeout = %d (config file default)", mp->alias, mp->pg_timeout); else condlog(3, "%s: pg_timeout = NONE (config file default)", mp->alias); return 0; } mp->pg_timeout = PGTIMEOUT_UNDEF; condlog(3, "pg_timeout = NONE (internal default)"); return 0; } extern int select_fast_io_fail(struct multipath *mp) { if (mp->hwe && mp->hwe->fast_io_fail) { mp->fast_io_fail = mp->hwe->fast_io_fail; if (mp->fast_io_fail == -1) condlog(3, "%s: fast_io_fail_tmo = off (controller default)", mp->alias); else condlog(3, "%s: fast_io_fail_tmo = %d (controller default)", mp->alias, mp->fast_io_fail); return 0; } if (conf->fast_io_fail) { mp->fast_io_fail = conf->fast_io_fail; if (mp->fast_io_fail == -1) condlog(3, "%s: fast_io_fail_tmo = off (config file default)", mp->alias); else condlog(3, "%s: fast_io_fail_tmo = %d (config file default)", mp->alias, mp->fast_io_fail); return 0; } mp->fast_io_fail = 0; return 0; } extern int select_dev_loss(struct multipath *mp) { if (mp->hwe && mp->hwe->dev_loss) { mp->dev_loss = mp->hwe->dev_loss; condlog(3, "%s: dev_loss_tmo = %u (controller default)", mp->alias, mp->dev_loss); return 0; } if (conf->dev_loss) { mp->dev_loss = conf->dev_loss; condlog(3, "%s: dev_loss_tmo = %u (config file default)", mp->alias, mp->dev_loss); return 0; } mp->dev_loss = 0; return 0; } extern int select_flush_on_last_del(struct multipath *mp) { if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) return 0; if (mp->mpe && mp->mpe->flush_on_last_del != FLUSH_UNDEF) { mp->flush_on_last_del = mp->mpe->flush_on_last_del; condlog(3, "flush_on_last_del = %i (multipath setting)", mp->flush_on_last_del); return 0; } if (mp->hwe && mp->hwe->flush_on_last_del != FLUSH_UNDEF) { mp->flush_on_last_del = mp->hwe->flush_on_last_del; condlog(3, "flush_on_last_del = %i (controler setting)", mp->flush_on_last_del); return 0; } if (conf->flush_on_last_del != FLUSH_UNDEF) { mp->flush_on_last_del = conf->flush_on_last_del; condlog(3, "flush_on_last_del = %i (config file default)", mp->flush_on_last_del); return 0; } mp->flush_on_last_del = FLUSH_UNDEF; condlog(3, "flush_on_last_del = DISABLED (internal default)"); return 0; } multipath-tools-0.4.9/libmultipath/propsel.h000066400000000000000000000014611137574366600212500ustar00rootroot00000000000000int 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_pg_timeout(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); multipath-tools-0.4.9/libmultipath/regex.c000066400000000000000000003411361137574366600206770ustar00rootroot00000000000000/* Extended regular expression matching and search library, version 0.12. (Implements POSIX draft P10003.2/D11.2, except for internationalization features.) Copyright (C) 1993 Free Software Foundation, 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, 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. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #ifndef bcmp #define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) #endif #ifndef bcopy #define bcopy(s, d, n) memcpy ((d), (s), (n)) #endif #ifndef bzero #define bzero(s, n) memset ((s), 0, (n)) #endif /* Define the syntax stuff for \<, \>, etc. */ #ifndef Sword #define Sword 1 #endif #define CHAR_SET_SIZE 256 static char re_syntax_table[CHAR_SET_SIZE]; static void init_syntax_once(void) { register int c; static int done = 0; if (done) return; bzero(re_syntax_table, sizeof re_syntax_table); for (c = 'a'; c <= 'z'; c++) re_syntax_table[c] = Sword; for (c = 'A'; c <= 'Z'; c++) re_syntax_table[c] = Sword; for (c = '0'; c <= '9'; c++) re_syntax_table[c] = Sword; re_syntax_table['_'] = Sword; done = 1; } #define SYNTAX(c) re_syntax_table[c] #include "regex.h" #include #ifdef isblank #define ISBLANK(c) (isascii (c) && isblank (c)) #else #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef isgraph #define ISGRAPH(c) (isascii (c) && isgraph (c)) #else #define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c)) #endif #define ISPRINT(c) (isascii (c) && isprint (c)) #define ISDIGIT(c) (isascii (c) && isdigit (c)) #define ISALNUM(c) (isascii (c) && isalnum (c)) #define ISALPHA(c) (isascii (c) && isalpha (c)) #define ISCNTRL(c) (isascii (c) && iscntrl (c)) #define ISLOWER(c) (isascii (c) && islower (c)) #define ISPUNCT(c) (isascii (c) && ispunct (c)) #define ISSPACE(c) (isascii (c) && isspace (c)) #define ISUPPER(c) (isascii (c) && isupper (c)) #define ISXDIGIT(c) (isascii (c) && isxdigit (c)) #undef SIGN_EXTEND_CHAR #define SIGN_EXTEND_CHAR(c) ((signed char) (c)) #ifndef alloca #ifdef __GNUC__ #define alloca __builtin_alloca #endif /* not __GNUC__ */ #endif /* not alloca */ #define REGEX_ALLOCATE alloca /* Assumes a `char *destination' variable. */ #define REGEX_REALLOCATE(source, osize, nsize) \ (destination = (char *) alloca (nsize), \ bcopy (source, destination, osize), \ destination) /* True if `size1' is non-NULL and PTR is pointing anywhere inside `string1' or just past its end. This works if PTR is NULL, which is a good thing. */ #define FIRST_STRING_P(ptr) \ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) /* (Re)Allocate N items of type T using malloc, or fail. */ #define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) #define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) #define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) #define BYTEWIDTH 8 /* In bits. */ #define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) typedef char boolean; #define false 0 #define true 1 typedef enum { no_op = 0, exactn = 1, anychar, charset, charset_not, start_memory, stop_memory, duplicate, begline, endline, begbuf, endbuf, jump, jump_past_alt, on_failure_jump, on_failure_keep_string_jump, pop_failure_jump, maybe_pop_jump, dummy_failure_jump, push_dummy_failure, succeed_n, jump_n, set_number_at, wordchar, notwordchar, wordbeg, wordend, wordbound, notwordbound } re_opcode_t; #define STORE_NUMBER(destination, number) \ do { \ (destination)[0] = (number) & 0377; \ (destination)[1] = (number) >> 8; \ } while (0) #define STORE_NUMBER_AND_INCR(destination, number) \ do { \ STORE_NUMBER (destination, number); \ (destination) += 2; \ } while (0) #define EXTRACT_NUMBER(destination, source) \ do { \ (destination) = *(source) & 0377; \ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ } while (0) #define EXTRACT_NUMBER_AND_INCR(destination, source) \ do { \ EXTRACT_NUMBER (destination, source); \ (source) += 2; \ } while (0) #undef assert #define assert(e) #define DEBUG_STATEMENT(e) #define DEBUG_PRINT1(x) #define DEBUG_PRINT2(x1, x2) #define DEBUG_PRINT3(x1, x2, x3) #define DEBUG_PRINT4(x1, x2, x3, x4) #define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) #define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; reg_syntax_t re_set_syntax(syntax) reg_syntax_t syntax; { reg_syntax_t ret = re_syntax_options; re_syntax_options = syntax; return ret; } /* This table gives an error message for each of the error codes listed in regex.h. Obviously the order here has to be same as there. */ static const char *re_error_msg[] = { NULL, /* REG_NOERROR */ "No match", /* REG_NOMATCH */ "Invalid regular expression", /* REG_BADPAT */ "Invalid collation character", /* REG_ECOLLATE */ "Invalid character class name", /* REG_ECTYPE */ "Trailing backslash", /* REG_EESCAPE */ "Invalid back reference", /* REG_ESUBREG */ "Unmatched [ or [^", /* REG_EBRACK */ "Unmatched ( or \\(", /* REG_EPAREN */ "Unmatched \\{", /* REG_EBRACE */ "Invalid content of \\{\\}", /* REG_BADBR */ "Invalid range end", /* REG_ERANGE */ "Memory exhausted", /* REG_ESPACE */ "Invalid preceding regular expression", /* REG_BADRPT */ "Premature end of regular expression", /* REG_EEND */ "Regular expression too big", /* REG_ESIZE */ "Unmatched ) or \\)", /* REG_ERPAREN */ }; /* Subroutine declarations and macros for regex_compile. */ static reg_errcode_t regex_compile (const char *pattern, size_t size, reg_syntax_t syntax, struct re_pattern_buffer * bufp); static void store_op1 (re_opcode_t op, unsigned char *loc, int arg); static void store_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2); static void insert_op1 (re_opcode_t op, unsigned char *loc, int arg, unsigned char *end); static void insert_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2, unsigned char *end); static boolean at_begline_loc_p (const char *pattern, const char *p, reg_syntax_t syntax); static boolean at_endline_loc_p (const char *p, const char *pend, reg_syntax_t syntax); static reg_errcode_t compile_range (const char **p_ptr, const char *pend, char *translate, reg_syntax_t syntax, unsigned char *b); /* Fetch the next character in the uncompiled pattern---translating it if necessary. Also cast from a signed character in the constant string passed to us by the user to an unsigned char that we can use as an array index (in, e.g., `translate'). */ #define PATFETCH(c) \ do {if (p == pend) return REG_EEND; \ c = (unsigned char) *p++; \ if (translate) c = translate[c]; \ } while (0) /* Fetch the next character in the uncompiled pattern, with no translation. */ #define PATFETCH_RAW(c) \ do {if (p == pend) return REG_EEND; \ c = (unsigned char) *p++; \ } while (0) /* Go backwards one character in the pattern. */ #define PATUNFETCH p-- /* If `translate' is non-null, return translate[D], else just D. We cast the subscript to translate because some data is declared as `char *', to avoid warnings when a string constant is passed. But when we use a character as a subscript we must make it unsigned. */ #define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d)) /* Macros for outputting the compiled pattern into `buffer'. */ /* If the buffer isn't allocated when it comes in, use this. */ #define INIT_BUF_SIZE 32 /* Make sure we have at least N more bytes of space in buffer. */ #define GET_BUFFER_SPACE(n) \ while (b - bufp->buffer + (n) > bufp->allocated) \ EXTEND_BUFFER () /* Make sure we have one more byte of buffer space and then add C to it. */ #define BUF_PUSH(c) \ do { \ GET_BUFFER_SPACE (1); \ *b++ = (unsigned char) (c); \ } while (0) /* Ensure we have two more bytes of buffer space and then append C1 and C2. */ #define BUF_PUSH_2(c1, c2) \ do { \ GET_BUFFER_SPACE (2); \ *b++ = (unsigned char) (c1); \ *b++ = (unsigned char) (c2); \ } while (0) /* As with BUF_PUSH_2, except for three bytes. */ #define BUF_PUSH_3(c1, c2, c3) \ do { \ GET_BUFFER_SPACE (3); \ *b++ = (unsigned char) (c1); \ *b++ = (unsigned char) (c2); \ *b++ = (unsigned char) (c3); \ } while (0) /* Store a jump with opcode OP at LOC to location TO. We store a relative address offset by the three bytes the jump itself occupies. */ #define STORE_JUMP(op, loc, to) \ store_op1 (op, loc, (int)((to) - (loc) - 3)) /* Likewise, for a two-argument jump. */ #define STORE_JUMP2(op, loc, to, arg) \ store_op2 (op, loc, (int)((to) - (loc) - 3), arg) /* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ #define INSERT_JUMP(op, loc, to) \ insert_op1 (op, loc, (int)((to) - (loc) - 3), b) /* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ #define INSERT_JUMP2(op, loc, to, arg) \ insert_op2 (op, loc, (int)((to) - (loc) - 3), arg, b) /* This is not an arbitrary limit: the arguments which represent offsets into the pattern are two bytes long. So if 2^16 bytes turns out to be too small, many things would have to change. */ #define MAX_BUF_SIZE (1L << 16) #define REALLOC realloc /* Extend the buffer by twice its current size via realloc and reset the pointers that pointed into the old block to point to the correct places in the new one. If extending the buffer results in it being larger than MAX_BUF_SIZE, then flag memory exhausted. */ #define EXTEND_BUFFER() \ do { \ unsigned char *old_buffer = bufp->buffer; \ if (bufp->allocated == MAX_BUF_SIZE) \ return REG_ESIZE; \ bufp->allocated <<= 1; \ if (bufp->allocated > MAX_BUF_SIZE) \ bufp->allocated = MAX_BUF_SIZE; \ bufp->buffer = (unsigned char *) REALLOC(bufp->buffer, bufp->allocated);\ if (bufp->buffer == NULL) \ return REG_ESPACE; \ /* If the buffer moved, move all the pointers into it. */ \ if (old_buffer != bufp->buffer) \ { \ b = (b - old_buffer) + bufp->buffer; \ begalt = (begalt - old_buffer) + bufp->buffer; \ if (fixup_alt_jump) \ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ if (laststart) \ laststart = (laststart - old_buffer) + bufp->buffer; \ if (pending_exact) \ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ } \ } while (0) /* Since we have one byte reserved for the register number argument to {start,stop}_memory, the maximum number of groups we can report things about is what fits in that byte. */ #define MAX_REGNUM 255 /* But patterns can have more than `MAX_REGNUM' registers. We just ignore the excess. */ typedef unsigned regnum_t; /* Macros for the compile stack. */ /* Since offsets can go either forwards or backwards, this type needs to be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ /* int may be not enough when sizeof(int) == 2 */ typedef long pattern_offset_t; typedef struct { pattern_offset_t begalt_offset; pattern_offset_t fixup_alt_jump; pattern_offset_t inner_group_offset; pattern_offset_t laststart_offset; regnum_t regnum; } compile_stack_elt_t; typedef struct { compile_stack_elt_t *stack; unsigned size; unsigned avail; /* Offset of next open position. */ } compile_stack_type; #define INIT_COMPILE_STACK_SIZE 32 #define COMPILE_STACK_EMPTY (compile_stack.avail == 0) #define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) /* The next available element. */ #define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) /* Set the bit for character C in a list. */ #define SET_LIST_BIT(c) \ (b[((unsigned char) (c)) / BYTEWIDTH] \ |= 1 << (((unsigned char) c) % BYTEWIDTH)) /* Get the next unsigned number in the uncompiled pattern. */ #define GET_UNSIGNED_NUMBER(num) \ { if (p != pend) \ { \ PATFETCH (c); \ while (ISDIGIT (c)) \ { \ if (num < 0) \ num = 0; \ num = num * 10 + c - '0'; \ if (p == pend) \ break; \ PATFETCH (c); \ } \ } \ } #define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ #define IS_CHAR_CLASS(string) \ (STREQ (string, "alpha") || STREQ (string, "upper") \ || STREQ (string, "lower") || STREQ (string, "digit") \ || STREQ (string, "alnum") || STREQ (string, "xdigit") \ || STREQ (string, "space") || STREQ (string, "print") \ || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) static boolean group_in_compile_stack (compile_stack_type compile_stack, regnum_t regnum); /* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. Returns one of error codes defined in `regex.h', or zero for success */ static reg_errcode_t regex_compile(pattern, size, syntax, bufp) const char *pattern; size_t size; reg_syntax_t syntax; struct re_pattern_buffer *bufp; { /* We fetch characters from PATTERN here. Even though PATTERN is `char *' (i.e., signed), we declare these variables as unsigned, so they can be reliably used as array indices. */ register unsigned char c, c1; /* A random tempory spot in PATTERN. */ const char *p1; /* Points to the end of the buffer, where we should append. */ register unsigned char *b; /* Keeps track of unclosed groups. */ compile_stack_type compile_stack; /* Points to the current (ending) position in the pattern. */ const char *p = pattern; const char *pend = pattern + size; /* How to translate the characters in the pattern. */ char *translate = bufp->translate; /* Address of the count-byte of the most recently inserted `exactn' command. This makes it possible to tell if a new exact-match character can be added to that command or if the character requires a new `exactn' command. */ unsigned char *pending_exact = 0; /* Address of start of the most recently finished expression. This tells, e.g., postfix * where to find the start of its operand. Reset at the beginning of groups and alternatives. */ unsigned char *laststart = 0; /* Address of beginning of regexp, or inside of last group. */ unsigned char *begalt; /* Place in the uncompiled pattern (i.e., the {) to which to go back if the interval is invalid. */ const char *beg_interval; /* Address of the place where a forward jump should go to the end of the containing expression. Each alternative of an `or' -- except the last -- ends with a forward jump of this sort. */ unsigned char *fixup_alt_jump = 0; /* Counts open-groups as they are encountered. Remembered for the matching close-group on the compile stack, so the same register number is put in the stop_memory as the start_memory. */ regnum_t regnum = 0; /* Initialize the compile stack. */ compile_stack.stack = TALLOC(INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); if (compile_stack.stack == NULL) return REG_ESPACE; compile_stack.size = INIT_COMPILE_STACK_SIZE; compile_stack.avail = 0; /* Initialize the pattern buffer. */ bufp->syntax = syntax; bufp->fastmap_accurate = 0; bufp->not_bol = bufp->not_eol = 0; /* Set `used' to zero, so that if we return an error, the pattern printer (for debugging) will think there's no pattern. We reset it at the end. */ bufp->used = 0; /* Always count groups, whether or not bufp->no_sub is set. */ bufp->re_nsub = 0; /* Initialize the syntax table. */ init_syntax_once(); if (bufp->allocated == 0) { if (bufp->buffer) { RETALLOC(bufp->buffer, INIT_BUF_SIZE, unsigned char); } else { /* Caller did not allocate a buffer. Do it for them. */ bufp->buffer = TALLOC(INIT_BUF_SIZE, unsigned char); } if (!bufp->buffer) return REG_ESPACE; bufp->allocated = INIT_BUF_SIZE; } begalt = b = bufp->buffer; /* Loop through the uncompiled pattern until we're at the end. */ while (p != pend) { PATFETCH(c); switch (c) { case '^': { if (p == pattern + 1 || syntax & RE_CONTEXT_INDEP_ANCHORS || at_begline_loc_p(pattern, p, syntax)) BUF_PUSH(begline); else goto normal_char; } break; case '$': { if (p == pend || syntax & RE_CONTEXT_INDEP_ANCHORS || at_endline_loc_p(p, pend, syntax)) BUF_PUSH(endline); else goto normal_char; } break; case '+': case '?': if ((syntax & RE_BK_PLUS_QM) || (syntax & RE_LIMITED_OPS)) goto normal_char; handle_plus: case '*': /* If there is no previous pattern... */ if (!laststart) { if (syntax & RE_CONTEXT_INVALID_OPS) return REG_BADRPT; else if (!(syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; } { /* Are we optimizing this jump? */ boolean keep_string_p = false; /* 1 means zero (many) matches is allowed. */ char zero_times_ok = 0, many_times_ok = 0; for (;;) { zero_times_ok |= c != '+'; many_times_ok |= c != '?'; if (p == pend) break; PATFETCH(c); if (c == '*' || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))); else if (syntax & RE_BK_PLUS_QM && c == '\\') { if (p == pend) return REG_EESCAPE; PATFETCH(c1); if (!(c1 == '+' || c1 == '?')) { PATUNFETCH; PATUNFETCH; break; } c = c1; } else { PATUNFETCH; break; } } if (!laststart) break; if (many_times_ok) { assert(p - 1 > pattern); /* Allocate the space for the jump. */ GET_BUFFER_SPACE(3); if (TRANSLATE(*(p - 2)) == TRANSLATE('.') && zero_times_ok && p < pend && TRANSLATE(*p) == TRANSLATE('\n') && !(syntax & RE_DOT_NEWLINE)) { /* We have .*\n. */ STORE_JUMP(jump, b, laststart); keep_string_p = true; } else STORE_JUMP(maybe_pop_jump, b, laststart - 3); b += 3; } GET_BUFFER_SPACE(3); INSERT_JUMP(keep_string_p ? on_failure_keep_string_jump : on_failure_jump, laststart, b + 3); pending_exact = 0; b += 3; if (!zero_times_ok) { GET_BUFFER_SPACE(3); INSERT_JUMP(dummy_failure_jump, laststart, laststart + 6); b += 3; } } break; case '.': laststart = b; BUF_PUSH(anychar); break; case '[': { boolean had_char_class = false; if (p == pend) return REG_EBRACK; GET_BUFFER_SPACE(34); laststart = b; /* We test `*p == '^' twice, instead of using an if statement, so we only need one BUF_PUSH. */ BUF_PUSH(*p == '^' ? charset_not : charset); if (*p == '^') p++; p1 = p; /* Push the number of bytes in the bitmap. */ BUF_PUSH((1 << BYTEWIDTH) / BYTEWIDTH); /* Clear the whole map. */ bzero(b, (1 << BYTEWIDTH) / BYTEWIDTH); if ((re_opcode_t) b[-2] == charset_not && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) SET_LIST_BIT('\n'); /* Read in characters and ranges, setting map bits. */ for (;;) { if (p == pend) return REG_EBRACK; PATFETCH(c); if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') { if (p == pend) return REG_EESCAPE; PATFETCH(c1); SET_LIST_BIT(c1); continue; } if (c == ']' && p != p1 + 1) break; if (had_char_class && c == '-' && *p != ']') return REG_ERANGE; if (c == '-' && !(p - 2 >= pattern && p[-2] == '[') && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') && *p != ']') { reg_errcode_t ret = compile_range(&p, pend, translate, syntax, b); if (ret != REG_NOERROR) return ret; } else if (p[0] == '-' && p[1] != ']') { reg_errcode_t ret; /* Move past the `-'. */ PATFETCH(c1); ret = compile_range(&p, pend, translate, syntax, b); if (ret != REG_NOERROR) return ret; } else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') { char str[CHAR_CLASS_MAX_LENGTH + 1]; PATFETCH(c); c1 = 0; /* If pattern is `[[:'. */ if (p == pend) return REG_EBRACK; for (;;) { PATFETCH(c); if (c == ':' || c == ']' || p == pend || c1 == CHAR_CLASS_MAX_LENGTH) break; str[c1++] = c; } str[c1] = '\0'; if (c == ':' && *p == ']') { int ch; boolean is_alnum = STREQ(str, "alnum"); boolean is_alpha = STREQ(str, "alpha"); boolean is_blank = STREQ(str, "blank"); boolean is_cntrl = STREQ(str, "cntrl"); boolean is_digit = STREQ(str, "digit"); boolean is_graph = STREQ(str, "graph"); boolean is_lower = STREQ(str, "lower"); boolean is_print = STREQ(str, "print"); boolean is_punct = STREQ(str, "punct"); boolean is_space = STREQ(str, "space"); boolean is_upper = STREQ(str, "upper"); boolean is_xdigit = STREQ(str, "xdigit"); if (!IS_CHAR_CLASS(str)) return REG_ECTYPE; PATFETCH(c); if (p == pend) return REG_EBRACK; for (ch = 0; ch < 1 << BYTEWIDTH; ch++) { if ((is_alnum && ISALNUM(ch)) || (is_alpha && ISALPHA(ch)) || (is_blank && ISBLANK(ch)) || (is_cntrl && ISCNTRL(ch)) || (is_digit && ISDIGIT(ch)) || (is_graph && ISGRAPH(ch)) || (is_lower && ISLOWER(ch)) || (is_print && ISPRINT(ch)) || (is_punct && ISPUNCT(ch)) || (is_space && ISSPACE(ch)) || (is_upper && ISUPPER(ch)) || (is_xdigit && ISXDIGIT(ch))) SET_LIST_BIT(ch); } had_char_class = true; } else { c1++; while (c1--) PATUNFETCH; SET_LIST_BIT('['); SET_LIST_BIT(':'); had_char_class = false; } } else { had_char_class = false; SET_LIST_BIT(c); } } while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) b[-1]--; b += b[-1]; } break; case '(': if (syntax & RE_NO_BK_PARENS) goto handle_open; else goto normal_char; case ')': if (syntax & RE_NO_BK_PARENS) goto handle_close; else goto normal_char; case '\n': if (syntax & RE_NEWLINE_ALT) goto handle_alt; else goto normal_char; case '|': if (syntax & RE_NO_BK_VBAR) goto handle_alt; else goto normal_char; case '{': if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) goto handle_interval; else goto normal_char; case '\\': if (p == pend) return REG_EESCAPE; PATFETCH_RAW(c); switch (c) { case '(': if (syntax & RE_NO_BK_PARENS) goto normal_backslash; handle_open: bufp->re_nsub++; regnum++; if (COMPILE_STACK_FULL) { RETALLOC(compile_stack.stack, compile_stack.size << 1, compile_stack_elt_t); if (compile_stack.stack == NULL) return REG_ESPACE; compile_stack.size <<= 1; } COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; COMPILE_STACK_TOP.fixup_alt_jump = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; COMPILE_STACK_TOP.regnum = regnum; if (regnum <= MAX_REGNUM) { COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; BUF_PUSH_3(start_memory, regnum, 0); } compile_stack.avail++; fixup_alt_jump = 0; laststart = 0; begalt = b; pending_exact = 0; break; case ')': if (syntax & RE_NO_BK_PARENS) goto normal_backslash; if (COMPILE_STACK_EMPTY) { if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) goto normal_backslash; else return REG_ERPAREN; } handle_close: if (fixup_alt_jump) { BUF_PUSH(push_dummy_failure); STORE_JUMP(jump_past_alt, fixup_alt_jump, b - 1); } if (COMPILE_STACK_EMPTY) { if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) goto normal_char; else return REG_ERPAREN; } assert(compile_stack.avail != 0); { regnum_t this_group_regnum; compile_stack.avail--; begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; fixup_alt_jump = COMPILE_STACK_TOP.fixup_alt_jump ? bufp->buffer + COMPILE_STACK_TOP. fixup_alt_jump - 1 : 0; laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; this_group_regnum = COMPILE_STACK_TOP.regnum; pending_exact = 0; if (this_group_regnum <= MAX_REGNUM) { unsigned char *inner_group_loc = bufp->buffer + COMPILE_STACK_TOP. inner_group_offset; *inner_group_loc = regnum - this_group_regnum; BUF_PUSH_3(stop_memory, this_group_regnum, regnum - this_group_regnum); } } break; case '|': /* `\|'. */ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) goto normal_backslash; handle_alt: if (syntax & RE_LIMITED_OPS) goto normal_char; GET_BUFFER_SPACE(3); INSERT_JUMP(on_failure_jump, begalt, b + 6); pending_exact = 0; b += 3; if (fixup_alt_jump) STORE_JUMP(jump_past_alt, fixup_alt_jump, b); fixup_alt_jump = b; GET_BUFFER_SPACE(3); b += 3; laststart = 0; begalt = b; break; case '{': /* If \{ is a literal. */ if (!(syntax & RE_INTERVALS) || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) || (p - 2 == pattern && p == pend)) goto normal_backslash; handle_interval: { int lower_bound = -1, upper_bound = -1; beg_interval = p - 1; if (p == pend) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else return REG_EBRACE; } GET_UNSIGNED_NUMBER(lower_bound); if (c == ',') { GET_UNSIGNED_NUMBER(upper_bound); if (upper_bound < 0) upper_bound = RE_DUP_MAX; } else upper_bound = lower_bound; if (lower_bound < 0 || upper_bound > RE_DUP_MAX || lower_bound > upper_bound) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else return REG_BADBR; } if (!(syntax & RE_NO_BK_BRACES)) { if (c != '\\') return REG_EBRACE; PATFETCH(c); } if (c != '}') { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else return REG_BADBR; } if (!laststart) { if (syntax & RE_CONTEXT_INVALID_OPS) return REG_BADRPT; else if (syntax & RE_CONTEXT_INDEP_OPS) laststart = b; else goto unfetch_interval; } if (upper_bound == 0) { GET_BUFFER_SPACE(3); INSERT_JUMP(jump, laststart, b + 3); b += 3; } else { unsigned nbytes = 10 + (upper_bound > 1) * 10; GET_BUFFER_SPACE(nbytes); INSERT_JUMP2(succeed_n, laststart, b + 5 + (upper_bound > 1) * 5, lower_bound); b += 5; insert_op2(set_number_at, laststart, 5, lower_bound, b); b += 5; if (upper_bound > 1) { STORE_JUMP2(jump_n, b, laststart + 5, upper_bound - 1); b += 5; insert_op2(set_number_at, laststart, b - laststart, upper_bound - 1, b); b += 5; } } pending_exact = 0; beg_interval = NULL; } break; unfetch_interval: assert(beg_interval); p = beg_interval; beg_interval = NULL; /* normal_char and normal_backslash need `c'. */ PATFETCH(c); if (!(syntax & RE_NO_BK_BRACES)) { if (p > pattern && p[-1] == '\\') goto normal_backslash; } goto normal_char; case 'w': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; laststart = b; BUF_PUSH(wordchar); break; case 'W': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; laststart = b; BUF_PUSH(notwordchar); break; case '<': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(wordbeg); break; case '>': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(wordend); break; case 'b': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(wordbound); break; case 'B': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(notwordbound); break; case '`': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(begbuf); break; case '\'': if (re_syntax_options & RE_NO_GNU_OPS) goto normal_char; BUF_PUSH(endbuf); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (syntax & RE_NO_BK_REFS) goto normal_char; c1 = c - '0'; if (c1 > regnum) return REG_ESUBREG; /* Can't back reference to a subexpression if inside of it. */ if (group_in_compile_stack (compile_stack, (regnum_t) c1)) goto normal_char; laststart = b; BUF_PUSH_2(duplicate, c1); break; case '+': case '?': if (syntax & RE_BK_PLUS_QM) goto handle_plus; else goto normal_backslash; default: normal_backslash: /* You might think it would be useful for \ to mean not to translate; but if we don't translate it it will never match anything. */ c = TRANSLATE(c); goto normal_char; } break; default: /* Expects the character in `c'. */ normal_char: /* If no exactn currently being built. */ if (!pending_exact /* If last exactn not at current position. */ || pending_exact + *pending_exact + 1 != b /* We have only one byte following the exactn for the count. */ || *pending_exact == (1 << BYTEWIDTH) - 1 /* If followed by a repetition operator. */ || *p == '*' || *p == '^' || ((syntax & RE_BK_PLUS_QM) ? *p == '\\' && (p[1] == '+' || p[1] == '?') : (*p == '+' || *p == '?')) || ((syntax & RE_INTERVALS) && ((syntax & RE_NO_BK_BRACES) ? *p == '{' : (p[0] == '\\' && p[1] == '{')))) { /* Start building a new exactn. */ laststart = b; BUF_PUSH_2(exactn, 0); pending_exact = b - 1; } BUF_PUSH(c); (*pending_exact)++; break; } /* switch (c) */ } /* while p != pend */ /* Through the pattern now. */ if (fixup_alt_jump) STORE_JUMP(jump_past_alt, fixup_alt_jump, b); if (!COMPILE_STACK_EMPTY) return REG_EPAREN; free(compile_stack.stack); /* We have succeeded; set the length of the buffer. */ bufp->used = b - bufp->buffer; return REG_NOERROR; } /* regex_compile */ /* Subroutines for `regex_compile'. */ /* Store OP at LOC followed by two-byte integer parameter ARG. */ static void store_op1(op, loc, arg) re_opcode_t op; unsigned char *loc; int arg; { *loc = (unsigned char) op; STORE_NUMBER(loc + 1, arg); } /* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ static void store_op2(op, loc, arg1, arg2) re_opcode_t op; unsigned char *loc; int arg1, arg2; { *loc = (unsigned char) op; STORE_NUMBER(loc + 1, arg1); STORE_NUMBER(loc + 3, arg2); } /* Copy the bytes from LOC to END to open up three bytes of space at LOC for OP followed by two-byte integer parameter ARG. */ static void insert_op1(op, loc, arg, end) re_opcode_t op; unsigned char *loc; int arg; unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 3; while (pfrom != loc) *--pto = *--pfrom; store_op1(op, loc, arg); } /* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ static void insert_op2(op, loc, arg1, arg2, end) re_opcode_t op; unsigned char *loc; int arg1, arg2; unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 5; while (pfrom != loc) *--pto = *--pfrom; store_op2(op, loc, arg1, arg2); } /* P points to just after a ^ in PATTERN. Return true if that ^ comes after an alternative or a begin-subexpression. We assume there is at least one character before the ^. */ static boolean at_begline_loc_p(pattern, p, syntax) const char *pattern, *p; reg_syntax_t syntax; { const char *prev = p - 2; boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; return /* After a subexpression? */ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) /* After an alternative? */ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); } /* The dual of at_begline_loc_p. This one is for $. We assume there is at least one character after the $, i.e., `P < PEND'. */ static boolean at_endline_loc_p(p, pend, syntax) const char *p, *pend; reg_syntax_t syntax; { const char *next = p; boolean next_backslash = *next == '\\'; const char *next_next = p + 1 < pend ? p + 1 : NULL; return /* Before a subexpression? */ (syntax & RE_NO_BK_PARENS ? *next == ')' : next_backslash && next_next && *next_next == ')') /* Before an alternative? */ || (syntax & RE_NO_BK_VBAR ? *next == '|' : next_backslash && next_next && *next_next == '|'); } /* Returns true if REGNUM is in one of COMPILE_STACK's elements and false if it's not. */ static boolean group_in_compile_stack(compile_stack, regnum) compile_stack_type compile_stack; regnum_t regnum; { int this_element; for (this_element = compile_stack.avail - 1; this_element >= 0; this_element--) if (compile_stack.stack[this_element].regnum == regnum) return true; return false; } /* Read the ending character of a range (in a bracket expression) from the uncompiled pattern *P_PTR (which ends at PEND). We assume the starting character is in `P[-2]'. (`P[-1]' is the character `-'.) Then we set the translation of all bits between the starting and ending characters (inclusive) in the compiled pattern B. Return an error code. We use these short variable names so we can use the same macros as `regex_compile' itself. */ static reg_errcode_t compile_range(p_ptr, pend, translate, syntax, b) const char **p_ptr, *pend; char *translate; reg_syntax_t syntax; unsigned char *b; { unsigned this_char; const char *p = *p_ptr; int range_start, range_end; if (p == pend) return REG_ERANGE; /* Even though the pattern is a signed `char *', we need to fetch with unsigned char *'s; if the high bit of the pattern character is set, the range endpoints will be negative if we fetch using a signed char *. We also want to fetch the endpoints without translating them; the appropriate translation is done in the bit-setting loop below. */ range_start = ((unsigned char *) p)[-2]; range_end = ((unsigned char *) p)[0]; /* Have to increment the pointer into the pattern string, so the caller isn't still at the ending character. */ (*p_ptr)++; /* If the start is after the end, the range is empty. */ if (range_start > range_end) return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; /* Here we see why `this_char' has to be larger than an `unsigned char' -- the range is inclusive, so if `range_end' == 0xff (assuming 8-bit characters), we would otherwise go into an infinite loop, since all characters <= 0xff. */ for (this_char = range_start; this_char <= range_end; this_char++) { SET_LIST_BIT(TRANSLATE(this_char)); } return REG_NOERROR; } /* Failure stack declarations and macros; both re_compile_fastmap and re_match_2 use a failure stack. These have to be macros because of REGEX_ALLOCATE. */ /* Number of failure points for which to initially allocate space when matching. If this number is exceeded, we allocate more space, so it is not a hard limit. */ #define INIT_FAILURE_ALLOC 5 /* Roughly the maximum number of failure points on the stack. Would be exactly that if always used MAX_FAILURE_SPACE each time we failed. This is a variable only so users of regex can assign to it; we never change it ourselves. */ int re_max_failures = 2000; typedef const unsigned char *fail_stack_elt_t; typedef struct { fail_stack_elt_t *stack; unsigned size; unsigned avail; /* Offset of next open position. */ } fail_stack_type; #define FAIL_STACK_EMPTY() (fail_stack.avail == 0) #define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) #define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) #define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail]) /* Initialize `fail_stack'. Do `return -2' if the alloc fails. */ #define INIT_FAIL_STACK() \ do { \ fail_stack.stack = (fail_stack_elt_t *) \ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ \ if (fail_stack.stack == NULL) \ return -2; \ \ fail_stack.size = INIT_FAILURE_ALLOC; \ fail_stack.avail = 0; \ } while (0) /* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. Return 1 if succeeds, and 0 if either ran out of memory allocating space for it or it was already too large. REGEX_REALLOCATE requires `destination' be declared. */ #define DOUBLE_FAIL_STACK(fail_stack) \ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \ ? 0 \ : ((fail_stack).stack = (fail_stack_elt_t *) \ REGEX_REALLOCATE ((fail_stack).stack, \ (fail_stack).size * sizeof (fail_stack_elt_t), \ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ \ (fail_stack).stack == NULL \ ? 0 \ : ((fail_stack).size <<= 1, \ 1))) /* Push PATTERN_OP on FAIL_STACK. Return 1 if was able to do so and 0 if ran out of memory allocating space to do so. */ #define PUSH_PATTERN_OP(pattern_op, fail_stack) \ ((FAIL_STACK_FULL () \ && !DOUBLE_FAIL_STACK (fail_stack)) \ ? 0 \ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \ 1)) /* This pushes an item onto the failure stack. Must be a four-byte value. Assumes the variable `fail_stack'. Probably should only be called from within `PUSH_FAILURE_POINT'. */ #define PUSH_FAILURE_ITEM(item) \ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item /* The complement operation. Assumes `fail_stack' is nonempty. */ #define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail] /* Used to omit pushing failure point id's when we're not debugging. */ #define DEBUG_PUSH(item) #define DEBUG_POP(item_addr) /* Push the information about the state we will need if we ever fail back to it. Requires variables fail_stack, regstart, regend, reg_info, and num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be declared. Does `return FAILURE_CODE' if runs out of memory. */ #define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ do { \ char *destination; \ /* Must be int, so when we don't save any registers, the arithmetic \ of 0 + -1 isn't done as unsigned. */ \ /* Can't be int, since there is not a shred of a guarantee that int \ is wide enough to hold a value of something to which pointer can \ be assigned */ \ s_reg_t this_reg; \ \ DEBUG_STATEMENT (failure_id++); \ DEBUG_STATEMENT (nfailure_points_pushed++); \ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ \ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ \ /* Ensure we have enough space allocated for what we will push. */ \ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ { \ if (!DOUBLE_FAIL_STACK (fail_stack)) \ return failure_code; \ \ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ (fail_stack).size); \ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ } #define PUSH_FAILURE_POINT2(pattern_place, string_place, failure_code) \ /* Push the info, starting with the registers. */ \ DEBUG_PRINT1 ("\n"); \ \ PUSH_FAILURE_POINT_LOOP (); \ \ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ PUSH_FAILURE_ITEM (lowest_active_reg); \ \ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ PUSH_FAILURE_ITEM (highest_active_reg); \ \ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ PUSH_FAILURE_ITEM (pattern_place); \ \ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ size2); \ DEBUG_PRINT1 ("'\n"); \ PUSH_FAILURE_ITEM (string_place); \ \ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ DEBUG_PUSH (failure_id); \ } while (0) /* Pulled out of PUSH_FAILURE_POINT() to shorten the definition of that macro. (for VAX C) */ #define PUSH_FAILURE_POINT_LOOP() \ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ this_reg++) \ { \ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ DEBUG_STATEMENT (num_regs_pushed++); \ \ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ PUSH_FAILURE_ITEM (regstart[this_reg]); \ \ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ PUSH_FAILURE_ITEM (regend[this_reg]); \ \ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ DEBUG_PRINT2 (" match_null=%d", \ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ DEBUG_PRINT2 (" matched_something=%d", \ MATCHED_SOMETHING (reg_info[this_reg])); \ DEBUG_PRINT2 (" ever_matched=%d", \ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ DEBUG_PRINT1 ("\n"); \ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \ } /* This is the number of items that are pushed and popped on the stack for each register. */ #define NUM_REG_ITEMS 3 /* Individual items aside from the registers. */ #define NUM_NONREG_ITEMS 4 /* We push at most this many items on the stack. */ #define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS) /* We actually push this many items. */ #define NUM_FAILURE_ITEMS \ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \ + NUM_NONREG_ITEMS) /* How many items can still be added to the stack without overflowing it. */ #define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) /* Pops what PUSH_FAIL_STACK pushes. We restore into the parameters, all of which should be lvalues: STR -- the saved data position. PAT -- the saved pattern position. LOW_REG, HIGH_REG -- the highest and lowest active registers. REGSTART, REGEND -- arrays of string positions. REG_INFO -- array of information about each subexpression. Also assumes the variables `fail_stack' and (if debugging), `bufp', `pend', `string1', `size1', `string2', and `size2'. */ #define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ { \ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ s_reg_t this_reg; \ const unsigned char *string_temp; \ \ assert (!FAIL_STACK_EMPTY ()); \ \ /* Remove failure points and point to how many regs pushed. */ \ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ \ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ \ DEBUG_POP (&failure_id); \ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ \ /* If the saved string location is NULL, it came from an \ on_failure_keep_string_jump opcode, and we want to throw away the \ saved NULL, thus retaining our current position in the string. */ \ string_temp = POP_FAILURE_ITEM (); \ if (string_temp != NULL) \ str = (const char *) string_temp; \ \ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ DEBUG_PRINT1 ("'\n"); \ \ pat = (unsigned char *) POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ \ POP_FAILURE_POINT2 (low_reg, high_reg, regstart, regend, reg_info); /* Pulled out of POP_FAILURE_POINT() to shorten the definition of that macro. (for MSC 5.1) */ #define POP_FAILURE_POINT2(low_reg, high_reg, regstart, regend, reg_info) \ \ /* Restore register info. */ \ high_reg = (active_reg_t) POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ \ low_reg = (active_reg_t) POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ \ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ { \ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ \ reg_info[this_reg].word = POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ \ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ \ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ } \ \ DEBUG_STATEMENT (nfailure_points_popped++); \ } /* POP_FAILURE_POINT */ /* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible characters can start a string that matches the pattern. This fastmap is used by re_search to skip quickly over impossible starting points. The caller must supply the address of a (1 << BYTEWIDTH)-byte data area as BUFP->fastmap. We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in the pattern buffer. Returns 0 if we succeed, -2 if an internal error. */ int re_compile_fastmap(bufp) struct re_pattern_buffer *bufp; { int j, k; fail_stack_type fail_stack; char *destination; /* We don't push any register information onto the failure stack. */ unsigned num_regs = 0; register char *fastmap = bufp->fastmap; unsigned char *pattern = bufp->buffer; const unsigned char *p = pattern; register unsigned char *pend = pattern + bufp->used; /* Assume that each path through the pattern can be null until proven otherwise. We set this false at the bottom of switch statement, to which we get only if a particular path doesn't match the empty string. */ boolean path_can_be_null = true; /* We aren't doing a `succeed_n' to begin with. */ boolean succeed_n_p = false; assert(fastmap != NULL && p != NULL); INIT_FAIL_STACK(); bzero(fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ bufp->fastmap_accurate = 1; /* It will be when we're done. */ bufp->can_be_null = 0; while (p != pend || !FAIL_STACK_EMPTY()) { if (p == pend) { bufp->can_be_null |= path_can_be_null; /* Reset for next path. */ path_can_be_null = true; p = fail_stack.stack[--fail_stack.avail]; } /* We should never be about to go beyond the end of the pattern. */ assert(p < pend); switch ((re_opcode_t) * p++) { /* I guess the idea here is to simply not bother with a fastmap if a backreference is used, since it's too hard to figure out the fastmap for the corresponding group. Setting `can_be_null' stops `re_search_2' from using the fastmap, so that is all we do. */ case duplicate: bufp->can_be_null = 1; return 0; /* Following are the cases which match a character. These end with `break'. */ case exactn: fastmap[p[1]] = 1; break; case charset: for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) fastmap[j] = 1; break; case charset_not: /* Chars beyond end of map must be allowed. */ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) fastmap[j] = 1; for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (! (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) fastmap[j] = 1; break; case wordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX(j) == Sword) fastmap[j] = 1; break; case notwordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX(j) != Sword) fastmap[j] = 1; break; case anychar: /* `.' matches anything ... */ for (j = 0; j < (1 << BYTEWIDTH); j++) fastmap[j] = 1; /* ... except perhaps newline. */ if (!(bufp->syntax & RE_DOT_NEWLINE)) fastmap['\n'] = 0; /* Return if we have already set `can_be_null'; if we have, then the fastmap is irrelevant. Something's wrong here. */ else if (bufp->can_be_null) return 0; /* Otherwise, have to check alternative paths. */ break; case no_op: case begline: case endline: case begbuf: case endbuf: case wordbound: case notwordbound: case wordbeg: case wordend: case push_dummy_failure: continue; case jump_n: case pop_failure_jump: case maybe_pop_jump: case jump: case jump_past_alt: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR(j, p); p += j; if (j > 0) continue; /* Jump backward implies we just went through the body of a loop and matched nothing. Opcode jumped to should be `on_failure_jump' or `succeed_n'. Just treat it like an ordinary jump. For a * loop, it has pushed its failure point already; if so, discard that as redundant. */ if ((re_opcode_t) * p != on_failure_jump && (re_opcode_t) * p != succeed_n) continue; p++; EXTRACT_NUMBER_AND_INCR(j, p); p += j; /* If what's on the stack is where we are now, pop it. */ if (!FAIL_STACK_EMPTY() && fail_stack.stack[fail_stack.avail - 1] == p) fail_stack.avail--; continue; case on_failure_jump: case on_failure_keep_string_jump: handle_on_failure_jump: EXTRACT_NUMBER_AND_INCR(j, p); /* For some patterns, e.g., `(a?)?', `p+j' here points to the end of the pattern. We don't want to push such a point, since when we restore it above, entering the switch will increment `p' past the end of the pattern. We don't need to push such a point since we obviously won't find any more fastmap entries beyond `pend'. Such a pattern can match the null string, though. */ if (p + j < pend) { if (!PUSH_PATTERN_OP(p + j, fail_stack)) return -2; } else bufp->can_be_null = 1; if (succeed_n_p) { EXTRACT_NUMBER_AND_INCR(k, p); /* Skip the n. */ succeed_n_p = false; } continue; case succeed_n: /* Get to the number of times to succeed. */ p += 2; /* Increment p past the n for when k != 0. */ EXTRACT_NUMBER_AND_INCR(k, p); if (k == 0) { p -= 4; succeed_n_p = true; /* Spaghetti code alert. */ goto handle_on_failure_jump; } continue; case set_number_at: p += 4; continue; case start_memory: case stop_memory: p += 2; continue; default: abort(); /* We have listed all the cases. */ } /* switch *p++ */ /* Getting here means we have found the possible starting characters for one path of the pattern -- and that the empty string does not match. We need not follow this path further. Instead, look at the next alternative (remembered on the stack), or quit if no more. The test at the top of the loop does these things. */ path_can_be_null = false; p = pend; } /* while p */ /* Set `can_be_null' for the last path (also the first path, if the pattern is empty). */ bufp->can_be_null |= path_can_be_null; return 0; } /* re_compile_fastmap */ /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated using the malloc library routine, and must each be at least NUM_REGS * sizeof (regoff_t) bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ void re_set_registers(bufp, regs, num_regs, starts, ends) struct re_pattern_buffer *bufp; struct re_registers *regs; unsigned num_regs; regoff_t *starts, *ends; { if (num_regs) { bufp->regs_allocated = REGS_REALLOCATE; regs->num_regs = num_regs; regs->start = starts; regs->end = ends; } else { bufp->regs_allocated = REGS_UNALLOCATED; regs->num_regs = 0; regs->start = regs->end = 0; } } /* Searching routines. */ /* Like re_search_2, below, but only one string is specified, and doesn't let you say where to stop matching. */ int re_search(bufp, string, size, startpos, range, regs) struct re_pattern_buffer *bufp; const char *string; int size, startpos, range; struct re_registers *regs; { return re_search_2(bufp, NULL, 0, string, size, startpos, range, regs, size); } /* Using the compiled pattern in BUFP->buffer, first tries to match the virtual concatenation of STRING1 and STRING2, starting first at index STARTPOS, then at STARTPOS + 1, and so on. STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. RANGE is how far to scan while trying to match. RANGE = 0 means try only at STARTPOS; in general, the last start tried is STARTPOS + RANGE. In REGS, return the indices of the virtual concatenation of STRING1 and STRING2 that matched the entire BUFP->buffer and its contained subexpressions. Do not consider matching one past the index STOP in the virtual concatenation of STRING1 and STRING2. We return either the position in the strings at which the match was found, -1 if no match, or -2 if error (such as failure stack overflow). */ int re_search_2(bufp, string1, size1, string2, size2, startpos, range, regs, stop) struct re_pattern_buffer *bufp; const char *string1, *string2; int size1, size2; int startpos; int range; struct re_registers *regs; int stop; { int val; register char *fastmap = bufp->fastmap; register char *translate = bufp->translate; int total_size = size1 + size2; int endpos = startpos + range; /* Check for out-of-range STARTPOS. */ if (startpos < 0 || startpos > total_size) return -1; /* Fix up RANGE if it might eventually take us outside the virtual concatenation of STRING1 and STRING2. */ if (endpos < -1) range = -1 - startpos; else if (endpos > total_size) range = total_size - startpos; /* If the search isn't to be a backwards one, don't waste time in a search for a pattern that must be anchored. */ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) { if (startpos > 0) return -1; else range = 1; } /* Update the fastmap now if not correct already. */ if (fastmap && !bufp->fastmap_accurate) if (re_compile_fastmap(bufp) == -2) return -2; /* Loop through the string, looking for a place to start matching. */ for (;;) { /* If a fastmap is supplied, skip quickly over characters that cannot be the start of a match. If the pattern can match the null string, however, we don't need to skip characters; we want the first null string. */ if (fastmap && startpos < total_size && !bufp->can_be_null) { if (range > 0) { /* Searching forwards. */ register const char *d; register int lim = 0; int irange = range; if (startpos < size1 && startpos + range >= size1) lim = range - (size1 - startpos); d = (startpos >= size1 ? string2 - size1 : string1) + startpos; /* Written out as an if-else to avoid testing `translate' inside the loop. */ if (translate) while (range > lim && !fastmap[(unsigned char) translate[(unsigned char) *d++]]) range--; else while (range > lim && !fastmap[(unsigned char) *d++]) range--; startpos += irange - range; } else { /* Searching backwards. */ register char c = (size1 == 0 || startpos >= size1 ? string2[startpos - size1] : string1[startpos]); if (!fastmap[(unsigned char) TRANSLATE(c)]) goto advance; } } /* If can't match the null string, and that's all we have left, fail. */ if (range >= 0 && startpos == total_size && fastmap && !bufp->can_be_null) return -1; val = re_match_2(bufp, string1, size1, string2, size2, startpos, regs, stop); if (val >= 0) return startpos; if (val == -2) return -2; advance: if (!range) break; else if (range > 0) { range--; startpos++; } else { range++; startpos--; } } return -1; } /* re_search_2 */ /* Structure for per-register (a.k.a. per-group) information. This must not be longer than one word, because we push this value onto the failure stack. Other register information, such as the starting and ending positions (which are addresses), and the list of inner groups (which is a bits list) are maintained in separate variables. We are making a (strictly speaking) nonportable assumption here: that the compiler will pack our bit fields into something that fits into the type of `word', i.e., is something that fits into one item on the failure stack. */ /* Declarations and macros for re_match_2. */ typedef union { fail_stack_elt_t word; struct { /* This field is one if this group can match the empty string, zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ #define MATCH_NULL_UNSET_VALUE 3 unsigned match_null_string_p:2; unsigned is_active:1; unsigned matched_something:1; unsigned ever_matched_something:1; } bits; } register_info_type; #define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) #define IS_ACTIVE(R) ((R).bits.is_active) #define MATCHED_SOMETHING(R) ((R).bits.matched_something) #define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) static boolean group_match_null_string_p (unsigned char **p, unsigned char *end, register_info_type * reg_info); static boolean alt_match_null_string_p (unsigned char *p, unsigned char *end, register_info_type * reg_info); static boolean common_op_match_null_string_p (unsigned char **p, unsigned char *end, register_info_type * reg_info); static int bcmp_translate (const char *s1, const char *s2, int len, char *translate); /* Call this when have matched a real character; it sets `matched' flags for the subexpressions which we are currently inside. Also records that those subexprs have matched. */ #define SET_REGS_MATCHED() \ do \ { \ active_reg_t r; \ for (r = lowest_active_reg; r <= highest_active_reg; r++) \ { \ MATCHED_SOMETHING (reg_info[r]) \ = EVER_MATCHED_SOMETHING (reg_info[r]) \ = 1; \ } \ } \ while (0) /* This converts PTR, a pointer into one of the search strings `string1' and `string2' into an offset from the beginning of that string. */ #define POINTER_TO_OFFSET(ptr) \ (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1) /* Registers are set to a sentinel when they haven't yet matched. */ #define REG_UNSET_VALUE ((char *) -1) #define REG_UNSET(e) ((e) == REG_UNSET_VALUE) /* Macros for dealing with the split strings in re_match_2. */ #define MATCHING_IN_FIRST_STRING (dend == end_match_1) /* Call before fetching a character with *d. This switches over to string2 if necessary. */ #define PREFETCH() \ while (d == dend) \ { \ /* End of string2 => fail. */ \ if (dend == end_match_2) \ goto fail; \ /* End of string1 => advance to string2. */ \ d = string2; \ dend = end_match_2; \ } /* Test if at very beginning or at very end of the virtual concatenation of `string1' and `string2'. If only one string, it's `string2'. */ #define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) #define AT_STRINGS_END(d) ((d) == end2) /* Test if D points to a character which is word-constituent. We have two special cases to check for: if past the end of string1, look at the first character in string2; and if before the beginning of string2, look at the last character in string1. */ #define WORDCHAR_P(d) \ (SYNTAX ((d) == end1 ? *string2 \ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ == Sword) /* Test if the character before D and the one at D differ with respect to being word-constituent. */ #define AT_WORD_BOUNDARY(d) \ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) /* Free everything we malloc. */ #define FREE_VARIABLES() alloca (0) /* These values must meet several constraints. They must not be valid register values; since we have a limit of 255 registers (because we use only one byte in the pattern for the register number), we can use numbers larger than 255. They must differ by 1, because of NUM_FAILURE_ITEMS above. And the value for the lowest register must be larger than the value for the highest register, so we do not try to actually save any registers when none are active. */ #define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) #define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) /* Matching routines. */ /* re_match is like re_match_2 except it takes only a single string. */ int re_match(bufp, string, size, pos, regs) struct re_pattern_buffer *bufp; const char *string; int size, pos; struct re_registers *regs; { return re_match_2(bufp, NULL, 0, string, size, pos, regs, size); } /* re_match_2 matches the compiled pattern in BUFP against the the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 and SIZE2, respectively). We start matching at POS, and stop matching at STOP. If REGS is non-null and the `no_sub' field of BUFP is nonzero, we store offsets for the substring each group matched in REGS. See the documentation for exactly how many groups we fill. We return -1 if no match, -2 if an internal error (such as the failure stack overflowing). Otherwise, we return the length of the matched substring. */ int re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) struct re_pattern_buffer *bufp; const char *string1, *string2; int size1, size2; int pos; struct re_registers *regs; int stop; { /* General temporaries. */ int mcnt; unsigned char *p1; /* Just past the end of the corresponding string. */ const char *end1, *end2; /* Pointers into string1 and string2, just past the last characters in each to consider matching. */ const char *end_match_1, *end_match_2; /* Where we are in the data, and the end of the current string. */ const char *d, *dend; /* Where we are in the pattern, and the end of the pattern. */ unsigned char *p = bufp->buffer; register unsigned char *pend = p + bufp->used; /* We use this to map every character in the string. */ char *translate = bufp->translate; /* Failure point stack. Each place that can handle a failure further down the line pushes a failure point on this stack. It consists of restart, regend, and reg_info for all registers corresponding to the subexpressions we're currently inside, plus the number of such registers, and, finally, two char *'s. The first char * is where to resume scanning the pattern; the second one is where to resume scanning the strings. If the latter is zero, the failure point is a ``dummy''; if a failure happens and the failure point is a dummy, it gets discarded and the next next one is tried. */ fail_stack_type fail_stack; /* We fill all the registers internally, independent of what we return, for use in backreferences. The number here includes an element for register zero. */ size_t num_regs = bufp->re_nsub + 1; /* The currently active registers. */ active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG; active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG; /* Information on the contents of registers. These are pointers into the input strings; they record just what was matched (on this attempt) by a subexpression part of the pattern, that is, the regnum-th regstart pointer points to where in the pattern we began matching and the regnum-th regend points to right after where we stopped matching the regnum-th subexpression. (The zeroth register keeps track of what the whole pattern matches.) */ const char **regstart = 0, **regend = 0; /* If a group that's operated upon by a repetition operator fails to match anything, then the register for its start will need to be restored because it will have been set to wherever in the string we are when we last see its open-group operator. Similarly for a register's end. */ const char **old_regstart = 0, **old_regend = 0; /* The is_active field of reg_info helps us keep track of which (possibly nested) subexpressions we are currently in. The matched_something field of reg_info[reg_num] helps us tell whether or not we have matched any of the pattern so far this time through the reg_num-th subexpression. These two fields get reset each time through any loop their register is in. */ register_info_type *reg_info = 0; /* The following record the register info as found in the above variables when we find a match better than any we've seen before. This happens as we backtrack through the failure points, which in turn happens only if we have not yet matched the entire string. */ unsigned best_regs_set = false; const char **best_regstart = 0, **best_regend = 0; /* Logically, this is `best_regend[0]'. But we don't want to have to allocate space for that if we're not allocating space for anything else (see below). Also, we never need info about register 0 for any of the other register vectors, and it seems rather a kludge to treat `best_regend' differently than the rest. So we keep track of the end of the best match so far in a separate variable. We initialize this to NULL so that when we backtrack the first time and need to test it, it's not garbage. */ const char *match_end = NULL; /* Used when we pop values we don't care about. */ const char **reg_dummy = 0; register_info_type *reg_info_dummy = 0; DEBUG_PRINT1("\n\nEntering re_match_2.\n"); INIT_FAIL_STACK(); /* Do not bother to initialize all the register variables if there are no groups in the pattern, as it takes a fair amount of time. If there are groups, we include space for register 0 (the whole pattern), even though we never use it, since it simplifies the array indexing. We should fix this. */ if (bufp->re_nsub) { regstart = REGEX_TALLOC(num_regs, const char *); regend = REGEX_TALLOC(num_regs, const char *); old_regstart = REGEX_TALLOC(num_regs, const char *); old_regend = REGEX_TALLOC(num_regs, const char *); best_regstart = REGEX_TALLOC(num_regs, const char *); best_regend = REGEX_TALLOC(num_regs, const char *); reg_info = REGEX_TALLOC(num_regs, register_info_type); reg_dummy = REGEX_TALLOC(num_regs, const char *); reg_info_dummy = REGEX_TALLOC(num_regs, register_info_type); if (! (regstart && regend && old_regstart && old_regend && reg_info && best_regstart && best_regend && reg_dummy && reg_info_dummy)) { FREE_VARIABLES(); return -2; } } /* The starting position is bogus. */ if (pos < 0 || pos > size1 + size2) { FREE_VARIABLES(); return -1; } /* Initialize subexpression text positions to -1 to mark ones that no start_memory/stop_memory has been seen for. Also initialize the register information struct. */ for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = regend[mcnt] = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; REG_MATCH_NULL_STRING_P(reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; IS_ACTIVE(reg_info[mcnt]) = 0; MATCHED_SOMETHING(reg_info[mcnt]) = 0; EVER_MATCHED_SOMETHING(reg_info[mcnt]) = 0; } /* We move `string1' into `string2' if the latter's empty -- but not if `string1' is null. */ if (size2 == 0 && string1 != NULL) { string2 = string1; size2 = size1; string1 = 0; size1 = 0; } end1 = string1 + size1; end2 = string2 + size2; /* Compute where to stop matching, within the two strings. */ if (stop <= size1) { end_match_1 = string1 + stop; end_match_2 = string2; } else { end_match_1 = end1; end_match_2 = string2 + stop - size1; } /* `p' scans through the pattern as `d' scans through the data. `dend' is the end of the input string that `d' points within. `d' is advanced into the following input string whenever necessary, but this happens before fetching; therefore, at the beginning of the loop, `d' can be pointing at the end of a string, but it cannot equal `string2'. */ if (size1 > 0 && pos <= size1) { d = string1 + pos; dend = end_match_1; } else { d = string2 + pos - size1; dend = end_match_2; } DEBUG_PRINT1("The compiled pattern is: "); DEBUG_PRINT_COMPILED_PATTERN(bufp, p, pend); DEBUG_PRINT1("The string to match is: `"); DEBUG_PRINT_DOUBLE_STRING(d, string1, size1, string2, size2); DEBUG_PRINT1("'\n"); /* This loops over pattern commands. It exits by returning from the function if the match is complete, or it drops through if the match fails at this starting point in the input data. */ for (;;) { DEBUG_PRINT2("\n0x%x: ", p); if (p == pend) { /* End of pattern means we might have succeeded. */ DEBUG_PRINT1("end of pattern ... "); /* If we haven't matched the entire string, and we want the longest match, try backtracking. */ if (d != end_match_2) { DEBUG_PRINT1("backtracking.\n"); if (!FAIL_STACK_EMPTY()) { /* More failure points to try. */ boolean same_str_p = (FIRST_STRING_P(match_end) == MATCHING_IN_FIRST_STRING); /* If exceeds best match so far, save it. */ if (!best_regs_set || (same_str_p && d > match_end) || (!same_str_p && !MATCHING_IN_FIRST_STRING)) { best_regs_set = true; match_end = d; DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); for (mcnt = 1; mcnt < num_regs; mcnt++) { best_regstart[mcnt] = regstart[mcnt]; best_regend[mcnt] = regend[mcnt]; } } goto fail; } /* If no failure points, don't restore garbage. */ else if (best_regs_set) { restore_best_regs: /* Restore best match. It may happen that `dend == end_match_1' while the restored d is in string2. For example, the pattern `x.*y.*z' against the strings `x-' and `y-z-', if the two strings are not consecutive in memory. */ DEBUG_PRINT1 ("Restoring best registers.\n"); d = match_end; dend = ((d >= string1 && d <= end1) ? end_match_1 : end_match_2); for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = best_regstart[mcnt]; regend[mcnt] = best_regend[mcnt]; } } } /* d != end_match_2 */ DEBUG_PRINT1("Accepting match.\n"); /* If caller wants register contents data back, do it. */ if (regs && !bufp->no_sub) { /* Have the register data arrays been allocated? */ if (bufp->regs_allocated == REGS_UNALLOCATED) { /* No. So allocate them with malloc. We need one extra element beyond `num_regs' for the `-1' marker GNU code uses. */ regs->num_regs = MAX(RE_NREGS, num_regs + 1); regs->start = TALLOC(regs->num_regs, regoff_t); regs->end = TALLOC(regs->num_regs, regoff_t); if (regs->start == NULL || regs->end == NULL) return -2; bufp->regs_allocated = REGS_REALLOCATE; } else if (bufp->regs_allocated == REGS_REALLOCATE) { /* Yes. If we need more elements than were already allocated, reallocate them. If we need fewer, just leave it alone. */ if (regs->num_regs < num_regs + 1) { regs->num_regs = num_regs + 1; RETALLOC(regs->start, regs->num_regs, regoff_t); RETALLOC(regs->end, regs->num_regs, regoff_t); if (regs->start == NULL || regs->end == NULL) return -2; } } else { /* These braces fend off a "empty body in an else-statement" warning under GCC when assert expands to nothing. */ assert(bufp->regs_allocated == REGS_FIXED); } /* Convert the pointer data in `regstart' and `regend' to indices. Register zero has to be set differently, since we haven't kept track of any info for it. */ if (regs->num_regs > 0) { regs->start[0] = pos; regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1 : d - string2 + size1); } /* Go through the first `min (num_regs, regs->num_regs)' registers, since that is all we initialized. */ for (mcnt = 1; mcnt < MIN(num_regs, regs->num_regs); mcnt++) { if (REG_UNSET(regstart[mcnt]) || REG_UNSET(regend[mcnt])) regs->start[mcnt] = regs->end[mcnt] = -1; else { regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]); regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]); } } /* If the regs structure we return has more elements than were in the pattern, set the extra elements to -1. If we (re)allocated the registers, this is the case, because we always allocate enough to have at least one -1 at the end. */ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) regs->start[mcnt] = regs->end[mcnt] = -1; } /* regs && !bufp->no_sub */ FREE_VARIABLES(); DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", nfailure_points_pushed, nfailure_points_popped, nfailure_points_pushed - nfailure_points_popped); DEBUG_PRINT2("%u registers pushed.\n", num_regs_pushed); mcnt = d - pos - (MATCHING_IN_FIRST_STRING ? string1 : string2 - size1); DEBUG_PRINT2("Returning %d from re_match_2.\n", mcnt); return mcnt; } /* Otherwise match next pattern command. */ switch ((re_opcode_t) * p++) { /* Ignore these. Used to ignore the n of succeed_n's which currently have n == 0. */ case no_op: DEBUG_PRINT1("EXECUTING no_op.\n"); break; /* Match the next n pattern characters exactly. The following byte in the pattern defines n, and the n bytes after that are the characters to match. */ case exactn: mcnt = *p++; DEBUG_PRINT2("EXECUTING exactn %d.\n", mcnt); /* This is written out as an if-else so we don't waste time testing `translate' inside the loop. */ if (translate) { do { PREFETCH(); if (translate[(unsigned char) *d++] != (char) *p++) goto fail; } while (--mcnt); } else { do { PREFETCH(); if (*d++ != (char) *p++) goto fail; } while (--mcnt); } SET_REGS_MATCHED(); break; /* Match any character except possibly a newline or a null. */ case anychar: DEBUG_PRINT1("EXECUTING anychar.\n"); PREFETCH(); if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE(*d) == '\n') || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE(*d) == '\000')) goto fail; SET_REGS_MATCHED(); DEBUG_PRINT2(" Matched `%d'.\n", *d); d++; break; case charset: case charset_not: { register unsigned char c; boolean not = (re_opcode_t) * (p - 1) == charset_not; DEBUG_PRINT2("EXECUTING charset%s.\n", not ? "_not" : ""); PREFETCH(); c = TRANSLATE(*d); /* The character to match. */ /* Cast to `unsigned' instead of `unsigned char' in case the bit list is a full 32 bytes long. */ if (c < (unsigned) (*p * BYTEWIDTH) && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; p += 1 + *p; if (!not) goto fail; SET_REGS_MATCHED(); d++; break; } /* The beginning of a group is represented by start_memory. The arguments are the register number in the next byte, and the number of groups inner to this one in the next. The text matched within the group is recorded (in the internal registers data structure) under the register number. */ case start_memory: DEBUG_PRINT3("EXECUTING start_memory %d (%d):\n", *p, p[1]); /* Find out if this group can match the empty string. */ p1 = p; /* To send to group_match_null_string_p. */ if (REG_MATCH_NULL_STRING_P(reg_info[*p]) == MATCH_NULL_UNSET_VALUE) REG_MATCH_NULL_STRING_P(reg_info[*p]) = group_match_null_string_p(&p1, pend, reg_info); /* Save the position in the string where we were the last time we were at this open-group operator in case the group is operated upon by a repetition operator, e.g., with `(a*)*b' against `ab'; then we want to ignore where we are now in the string in case this attempt to match fails. */ old_regstart[*p] = REG_MATCH_NULL_STRING_P(reg_info[*p]) ? REG_UNSET(regstart[*p]) ? d : regstart[*p] : regstart[*p]; DEBUG_PRINT2(" old_regstart: %d\n", POINTER_TO_OFFSET(old_regstart[*p])); regstart[*p] = d; DEBUG_PRINT2(" regstart: %d\n", POINTER_TO_OFFSET(regstart[*p])); IS_ACTIVE(reg_info[*p]) = 1; MATCHED_SOMETHING(reg_info[*p]) = 0; /* This is the new highest active register. */ highest_active_reg = *p; /* If nothing was active before, this is the new lowest active register. */ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) lowest_active_reg = *p; /* Move past the register number and inner group count. */ p += 2; break; /* The stop_memory opcode represents the end of a group. Its arguments are the same as start_memory's: the register number, and the number of inner groups. */ case stop_memory: DEBUG_PRINT3("EXECUTING stop_memory %d (%d):\n", *p, p[1]); /* We need to save the string position the last time we were at this close-group operator in case the group is operated upon by a repetition operator, e.g., with `((a*)*(b*)*)*' against `aba'; then we want to ignore where we are now in the string in case this attempt to match fails. */ old_regend[*p] = REG_MATCH_NULL_STRING_P(reg_info[*p]) ? REG_UNSET(regend[*p]) ? d : regend[*p] : regend[*p]; DEBUG_PRINT2(" old_regend: %d\n", POINTER_TO_OFFSET(old_regend[*p])); regend[*p] = d; DEBUG_PRINT2(" regend: %d\n", POINTER_TO_OFFSET(regend[*p])); /* This register isn't active anymore. */ IS_ACTIVE(reg_info[*p]) = 0; /* If this was the only register active, nothing is active anymore. */ if (lowest_active_reg == highest_active_reg) { lowest_active_reg = NO_LOWEST_ACTIVE_REG; highest_active_reg = NO_HIGHEST_ACTIVE_REG; } else { /* We must scan for the new highest active register, since it isn't necessarily one less than now: consider (a(b)c(d(e)f)g). When group 3 ends, after the f), the new highest active register is 1. */ unsigned char r = *p - 1; while (r > 0 && !IS_ACTIVE(reg_info[r])) r--; /* If we end up at register zero, that means that we saved the registers as the result of an `on_failure_jump', not a `start_memory', and we jumped to past the innermost `stop_memory'. For example, in ((.)*) we save registers 1 and 2 as a result of the *, but when we pop back to the second ), we are at the stop_memory 1. Thus, nothing is active. */ if (r == 0) { lowest_active_reg = NO_LOWEST_ACTIVE_REG; highest_active_reg = NO_HIGHEST_ACTIVE_REG; } else highest_active_reg = r; } /* If just failed to match something this time around with a group that's operated on by a repetition operator, try to force exit from the ``loop'', and restore the register information for this group that we had before trying this last match. */ if ((!MATCHED_SOMETHING(reg_info[*p]) || (re_opcode_t) p[-3] == start_memory) && (p + 2) < pend) { boolean is_a_jump_n = false; p1 = p + 2; mcnt = 0; switch ((re_opcode_t) * p1++) { case jump_n: is_a_jump_n = true; case pop_failure_jump: case maybe_pop_jump: case jump: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR(mcnt, p1); if (is_a_jump_n) p1 += 2; break; default: /* do nothing */ ; } p1 += mcnt; /* If the next operation is a jump backwards in the pattern to an on_failure_jump right before the start_memory corresponding to this stop_memory, exit from the loop by forcing a failure after pushing on the stack the on_failure_jump's jump in the pattern, and d. */ if (mcnt < 0 && (re_opcode_t) * p1 == on_failure_jump && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) { /* If this group ever matched anything, then restore what its registers were before trying this last failed match, e.g., with `(a*)*b' against `ab' for regstart[1], and, e.g., with `((a*)*(b*)*)*' against `aba' for regend[3]. Also restore the registers for inner groups for, e.g., `((a*)(b*))*' against `aba' (register 3 would otherwise get trashed). */ if (EVER_MATCHED_SOMETHING (reg_info[*p])) { unsigned r; EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; /* Restore this and inner groups' (if any) registers. */ for (r = *p; r < *p + *(p + 1); r++) { regstart[r] = old_regstart [r]; /* xx why this test? */ if ((s_reg_t) old_regend[r] >= (s_reg_t) regstart[r]) regend[r] = old_regend [r]; } } p1++; EXTRACT_NUMBER_AND_INCR(mcnt, p1); PUSH_FAILURE_POINT(p1 + mcnt, d, -2); PUSH_FAILURE_POINT2(p1 + mcnt, d, -2); goto fail; } } /* Move past the register number and the inner group count. */ p += 2; break; /* \ has been turned into a `duplicate' command which is followed by the numeric value of as the register number. */ case duplicate: { register const char *d2, *dend2; int regno = *p++; /* Get which register to match against. */ DEBUG_PRINT2("EXECUTING duplicate %d.\n", regno); /* Can't back reference a group which we've never matched. */ if (REG_UNSET(regstart[regno]) || REG_UNSET(regend[regno])) goto fail; /* Where in input to try to start matching. */ d2 = regstart[regno]; /* Where to stop matching; if both the place to start and the place to stop matching are in the same string, then set to the place to stop, otherwise, for now have to use the end of the first string. */ dend2 = ((FIRST_STRING_P(regstart[regno]) == FIRST_STRING_P(regend[regno])) ? regend[regno] : end_match_1); for (;;) { /* If necessary, advance to next segment in register contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; if (dend2 == regend[regno]) break; /* End of string1 => advance to string2. */ d2 = string2; dend2 = regend[regno]; } /* At end of register contents => success */ if (d2 == dend2) break; /* If necessary, advance to next segment in data. */ PREFETCH(); /* How many characters left in this segment to match. */ mcnt = dend - d; /* Want how many consecutive characters we can match in one shot, so, if necessary, adjust the count. */ if (mcnt > dend2 - d2) mcnt = dend2 - d2; /* Compare that many; failure if mismatch, else move past them. */ if (translate ? bcmp_translate(d, d2, mcnt, translate) : bcmp(d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; } } break; /* begline matches the empty string at the beginning of the string (unless `not_bol' is set in `bufp'), and, if `newline_anchor' is set, after newlines. */ case begline: DEBUG_PRINT1("EXECUTING begline.\n"); if (AT_STRINGS_BEG(d)) { if (!bufp->not_bol) break; } else if (d[-1] == '\n' && bufp->newline_anchor) { break; } /* In all other cases, we fail. */ goto fail; /* endline is the dual of begline. */ case endline: DEBUG_PRINT1("EXECUTING endline.\n"); if (AT_STRINGS_END(d)) { if (!bufp->not_eol) break; } /* We have to ``prefetch'' the next character. */ else if ((d == end1 ? *string2 : *d) == '\n' && bufp->newline_anchor) { break; } goto fail; /* Match at the very beginning of the data. */ case begbuf: DEBUG_PRINT1("EXECUTING begbuf.\n"); if (AT_STRINGS_BEG(d)) break; goto fail; /* Match at the very end of the data. */ case endbuf: DEBUG_PRINT1("EXECUTING endbuf.\n"); if (AT_STRINGS_END(d)) break; goto fail; /* on_failure_keep_string_jump is used to optimize `.*\n'. It pushes NULL as the value for the string on the stack. Then `pop_failure_point' will keep the current value for the string, instead of restoring it. To see why, consider matching `foo\nbar' against `.*\n'. The .* matches the foo; then the . fails against the \n. But the next thing we want to do is match the \n against the \n; if we restored the string value, we would be back at the foo. Because this is used only in specific cases, we don't need to check all the things that `on_failure_jump' does, to make sure the right things get saved on the stack. Hence we don't share its code. The only reason to push anything on the stack at all is that otherwise we would have to change `anychar's code to do something besides goto fail in this case; that seems worse than this. */ case on_failure_keep_string_jump: DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); EXTRACT_NUMBER_AND_INCR(mcnt, p); DEBUG_PRINT3(" %d (to 0x%x):\n", mcnt, p + mcnt); PUSH_FAILURE_POINT(p + mcnt, NULL, -2); PUSH_FAILURE_POINT2(p + mcnt, NULL, -2); break; /* Uses of on_failure_jump: Each alternative starts with an on_failure_jump that points to the beginning of the next alternative. Each alternative except the last ends with a jump that in effect jumps past the rest of the alternatives. (They really jump to the ending jump of the following alternative, because tensioning these jumps is a hassle.) Repeats start with an on_failure_jump that points past both the repetition text and either the following jump or pop_failure_jump back to this on_failure_jump. */ case on_failure_jump: on_failure: DEBUG_PRINT1("EXECUTING on_failure_jump"); EXTRACT_NUMBER_AND_INCR(mcnt, p); DEBUG_PRINT3(" %d (to 0x%x)", mcnt, p + mcnt); /* If this on_failure_jump comes right before a group (i.e., the original * applied to a group), save the information for that group and all inner ones, so that if we fail back to this point, the group's information will be correct. For example, in \(a*\)*\1, we need the preceding group, and in \(\(a*\)b*\)\2, we need the inner group. */ /* We can't use `p' to check ahead because we push a failure point to `p + mcnt' after we do this. */ p1 = p; /* We need to skip no_op's before we look for the start_memory in case this on_failure_jump is happening as the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 against aba. */ while (p1 < pend && (re_opcode_t) * p1 == no_op) p1++; if (p1 < pend && (re_opcode_t) * p1 == start_memory) { /* We have a new highest active register now. This will get reset at the start_memory we are about to get to, but we will have saved all the registers relevant to this repetition op, as described above. */ highest_active_reg = *(p1 + 1) + *(p1 + 2); if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) lowest_active_reg = *(p1 + 1); } DEBUG_PRINT1(":\n"); PUSH_FAILURE_POINT(p + mcnt, d, -2); PUSH_FAILURE_POINT2(p + mcnt, d, -2); break; /* A smart repeat ends with `maybe_pop_jump'. We change it to either `pop_failure_jump' or `jump'. */ case maybe_pop_jump: EXTRACT_NUMBER_AND_INCR(mcnt, p); DEBUG_PRINT2("EXECUTING maybe_pop_jump %d.\n", mcnt); { register unsigned char *p2 = p; /* Compare the beginning of the repeat with what in the pattern follows its end. If we can establish that there is nothing that they would both match, i.e., that we would have to backtrack because of (as in, e.g., `a*a') then we can change to pop_failure_jump, because we'll never have to backtrack. This is not true in the case of alternatives: in `(a|ab)*' we do need to backtrack to the `ab' alternative (e.g., if the string was `ab'). But instead of trying to detect that here, the alternative has put on a dummy failure point which is what we will end up popping. */ /* Skip over open/close-group commands. */ while (p2 + 2 < pend && ((re_opcode_t) * p2 == stop_memory || (re_opcode_t) * p2 == start_memory)) p2 += 3; /* Skip over args, too. */ /* If we're at the end of the pattern, we can change. */ if (p2 == pend) { /* Consider what happens when matching ":\(.*\)" against ":/". I don't really understand this code yet. */ p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" End of pattern: change to `pop_failure_jump'.\n"); } else if ((re_opcode_t) * p2 == exactn || (bufp->newline_anchor && (re_opcode_t) * p2 == endline)) { register unsigned char c = *p2 == (unsigned char) endline ? '\n' : p2[2]; p1 = p + mcnt; /* p1[0] ... p1[2] are the `on_failure_jump' corresponding to the `maybe_finalize_jump' of this case. Examine what follows. */ if ((re_opcode_t) p1[3] == exactn && p1[5] != c) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", c, p1[5]); } else if ((re_opcode_t) p1[3] == charset || (re_opcode_t) p1[3] == charset_not) { int not = (re_opcode_t) p1[3] == charset_not; if (c < (unsigned char) (p1[4] * BYTEWIDTH) && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; /* `not' is equal to 1 if c would match, which means that we can't change to pop_failure_jump. */ if (!not) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); } } } } p -= 2; /* Point at relative address again. */ if ((re_opcode_t) p[-1] != pop_failure_jump) { p[-1] = (unsigned char) jump; DEBUG_PRINT1(" Match => jump.\n"); goto unconditional_jump; } /* Note fall through. */ /* The end of a simple repeat has a pop_failure_jump back to its matching on_failure_jump, where the latter will push a failure point. The pop_failure_jump takes off failure points put on by this pop_failure_jump's matching on_failure_jump; we got through the pattern to here from the matching on_failure_jump, so didn't fail. */ case pop_failure_jump: { /* We need to pass separate storage for the lowest and highest registers, even though we don't care about the actual values. Otherwise, we will restore only one register from the stack, since lowest will == highest in `pop_failure_point'. */ active_reg_t dummy_low_reg, dummy_high_reg; unsigned char *pdummy; const char *sdummy; DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); POP_FAILURE_POINT(sdummy, pdummy, dummy_low_reg, dummy_high_reg, reg_dummy, reg_dummy, reg_info_dummy); } /* Note fall through. */ /* Unconditionally jump (without popping any failure points). */ case jump: unconditional_jump: EXTRACT_NUMBER_AND_INCR(mcnt, p); /* Get the amount to jump. */ DEBUG_PRINT2("EXECUTING jump %d ", mcnt); p += mcnt; /* Do the jump. */ DEBUG_PRINT2("(to 0x%x).\n", p); break; /* We need this opcode so we can detect where alternatives end in `group_match_null_string_p' et al. */ case jump_past_alt: DEBUG_PRINT1("EXECUTING jump_past_alt.\n"); goto unconditional_jump; /* Normally, the on_failure_jump pushes a failure point, which then gets popped at pop_failure_jump. We will end up at pop_failure_jump, also, and with a pattern of, say, `a+', we are skipping over the on_failure_jump, so we have to push something meaningless for pop_failure_jump to pop. */ case dummy_failure_jump: DEBUG_PRINT1("EXECUTING dummy_failure_jump.\n"); /* It doesn't matter what we push for the string here. What the code at `fail' tests is the value for the pattern. */ PUSH_FAILURE_POINT(0, 0, -2); PUSH_FAILURE_POINT2(0, 0, -2); goto unconditional_jump; /* At the end of an alternative, we need to push a dummy failure point in case we are followed by a `pop_failure_jump', because we don't want the failure point for the alternative to be popped. For example, matching `(a|ab)*' against `aab' requires that we match the `ab' alternative. */ case push_dummy_failure: DEBUG_PRINT1("EXECUTING push_dummy_failure.\n"); /* See comments just above at `dummy_failure_jump' about the two zeroes. */ PUSH_FAILURE_POINT(0, 0, -2); PUSH_FAILURE_POINT2(0, 0, -2); break; /* Have to succeed matching what follows at least n times. After that, handle like `on_failure_jump'. */ case succeed_n: EXTRACT_NUMBER(mcnt, p + 2); DEBUG_PRINT2("EXECUTING succeed_n %d.\n", mcnt); assert(mcnt >= 0); /* Originally, this is how many times we HAVE to succeed. */ if (mcnt > 0) { mcnt--; p += 2; STORE_NUMBER_AND_INCR(p, mcnt); DEBUG_PRINT3(" Setting 0x%x to %d.\n", p, mcnt); } else if (mcnt == 0) { DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p + 2); p[2] = (unsigned char) no_op; p[3] = (unsigned char) no_op; goto on_failure; } break; case jump_n: EXTRACT_NUMBER(mcnt, p + 2); DEBUG_PRINT2("EXECUTING jump_n %d.\n", mcnt); /* Originally, this is how many times we CAN jump. */ if (mcnt) { mcnt--; STORE_NUMBER(p + 2, mcnt); goto unconditional_jump; } /* If don't have to jump any more, skip over the rest of command. */ else p += 4; break; case set_number_at: { DEBUG_PRINT1("EXECUTING set_number_at.\n"); EXTRACT_NUMBER_AND_INCR(mcnt, p); p1 = p + mcnt; EXTRACT_NUMBER_AND_INCR(mcnt, p); DEBUG_PRINT3(" Setting 0x%x to %d.\n", p1, mcnt); STORE_NUMBER(p1, mcnt); break; } case wordbound: DEBUG_PRINT1("EXECUTING wordbound.\n"); if (AT_WORD_BOUNDARY(d)) break; goto fail; case notwordbound: DEBUG_PRINT1("EXECUTING notwordbound.\n"); if (AT_WORD_BOUNDARY(d)) goto fail; break; case wordbeg: DEBUG_PRINT1("EXECUTING wordbeg.\n"); if (WORDCHAR_P(d) && (AT_STRINGS_BEG(d) || !WORDCHAR_P(d - 1))) break; goto fail; case wordend: DEBUG_PRINT1("EXECUTING wordend.\n"); if (!AT_STRINGS_BEG(d) && WORDCHAR_P(d - 1) && (!WORDCHAR_P(d) || AT_STRINGS_END(d))) break; goto fail; case wordchar: DEBUG_PRINT1("EXECUTING non-Emacs wordchar.\n"); PREFETCH(); if (!WORDCHAR_P(d)) goto fail; SET_REGS_MATCHED(); d++; break; case notwordchar: DEBUG_PRINT1("EXECUTING non-Emacs notwordchar.\n"); PREFETCH(); if (WORDCHAR_P(d)) goto fail; SET_REGS_MATCHED(); d++; break; default: abort(); } continue; /* Successfully executed one pattern command; keep going. */ /* We goto here if a matching operation fails. */ fail: if (!FAIL_STACK_EMPTY()) { /* A restart point is known. Restore to that state. */ DEBUG_PRINT1("\nFAIL:\n"); POP_FAILURE_POINT(d, p, lowest_active_reg, highest_active_reg, regstart, regend, reg_info); /* If this failure point is a dummy, try the next one. */ if (!p) goto fail; /* If we failed to the end of the pattern, don't examine *p. */ assert(p <= pend); if (p < pend) { boolean is_a_jump_n = false; /* If failed to a backwards jump that's part of a repetition loop, need to pop this failure point and use the next one. */ switch ((re_opcode_t) * p) { case jump_n: is_a_jump_n = true; case maybe_pop_jump: case pop_failure_jump: case jump: p1 = p + 1; EXTRACT_NUMBER_AND_INCR(mcnt, p1); p1 += mcnt; if ((is_a_jump_n && (re_opcode_t) * p1 == succeed_n) || (!is_a_jump_n && (re_opcode_t) * p1 == on_failure_jump)) goto fail; break; default: /* do nothing */ ; } } if (d >= string1 && d <= end1) dend = end_match_1; } else break; /* Matching at this starting point really fails. */ } /* for (;;) */ if (best_regs_set) goto restore_best_regs; FREE_VARIABLES(); return -1; /* Failure to match. */ } /* re_match_2 */ /* Subroutine definitions for re_match_2. */ /* We are passed P pointing to a register number after a start_memory. Return true if the pattern up to the corresponding stop_memory can match the empty string, and false otherwise. If we find the matching stop_memory, sets P to point to one past its number. Otherwise, sets P to an undefined byte less than or equal to END. We don't handle duplicates properly (yet). */ static boolean group_match_null_string_p(p, end, reg_info) unsigned char **p, *end; register_info_type *reg_info; { int mcnt; /* Point to after the args to the start_memory. */ unsigned char *p1 = *p + 2; while (p1 < end) { /* Skip over opcodes that can match nothing, and return true or false, as appropriate, when we get to one that can't, or to the matching stop_memory. */ switch ((re_opcode_t) * p1) { /* Could be either a loop or a series of alternatives. */ case on_failure_jump: p1++; EXTRACT_NUMBER_AND_INCR(mcnt, p1); /* If the next operation is not a jump backwards in the pattern. */ if (mcnt >= 0) { /* Go through the on_failure_jumps of the alternatives, seeing if any of the alternatives cannot match nothing. The last alternative starts with only a jump, whereas the rest start with on_failure_jump and end with a jump, e.g., here is the pattern for `a|b|c': /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 /exactn/1/c So, we have to first go through the first (n-1) alternatives and then deal with the last one separately. */ /* Deal with the first (n-1) alternatives, which start with an on_failure_jump (see above) that jumps to right past a jump_past_alt. */ while ((re_opcode_t) p1[mcnt - 3] == jump_past_alt) { /* `mcnt' holds how many bytes long the alternative is, including the ending `jump_past_alt' and its number. */ if (!alt_match_null_string_p (p1, p1 + mcnt - 3, reg_info)) return false; /* Move to right after this alternative, including the jump_past_alt. */ p1 += mcnt; /* Break if it's the beginning of an n-th alternative that doesn't begin with an on_failure_jump. */ if ((re_opcode_t) * p1 != on_failure_jump) break; /* Still have to check that it's not an n-th alternative that starts with an on_failure_jump. */ p1++; EXTRACT_NUMBER_AND_INCR(mcnt, p1); if ((re_opcode_t) p1[mcnt - 3] != jump_past_alt) { /* Get to the beginning of the n-th alternative. */ p1 -= 3; break; } } /* Deal with the last alternative: go back and get number of the `jump_past_alt' just before it. `mcnt' contains the length of the alternative. */ EXTRACT_NUMBER(mcnt, p1 - 2); if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) return false; p1 += mcnt; /* Get past the n-th alternative. */ } /* if mcnt > 0 */ break; case stop_memory: assert(p1[1] == **p); *p = p1 + 2; return true; default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } } /* while p1 < end */ return false; } /* group_match_null_string_p */ /* Similar to group_match_null_string_p, but doesn't deal with alternatives: It expects P to be the first byte of a single alternative and END one byte past the last. The alternative can contain groups. */ static boolean alt_match_null_string_p(p, end, reg_info) unsigned char *p, *end; register_info_type *reg_info; { int mcnt; unsigned char *p1 = p; while (p1 < end) { /* Skip over opcodes that can match nothing, and break when we get to one that can't. */ switch ((re_opcode_t) * p1) { /* It's a loop. */ case on_failure_jump: p1++; EXTRACT_NUMBER_AND_INCR(mcnt, p1); p1 += mcnt; break; default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } } /* while p1 < end */ return true; } /* alt_match_null_string_p */ /* Deals with the ops common to group_match_null_string_p and alt_match_null_string_p. Sets P to one after the op and its arguments, if any. */ static boolean common_op_match_null_string_p(p, end, reg_info) unsigned char **p, *end; register_info_type *reg_info; { int mcnt; boolean ret; int reg_no; unsigned char *p1 = *p; switch ((re_opcode_t) * p1++) { case no_op: case begline: case endline: case begbuf: case endbuf: case wordbeg: case wordend: case wordbound: case notwordbound: break; case start_memory: reg_no = *p1; assert(reg_no > 0 && reg_no <= MAX_REGNUM); ret = group_match_null_string_p(&p1, end, reg_info); /* Have to set this here in case we're checking a group which contains a group and a back reference to it. */ if (REG_MATCH_NULL_STRING_P(reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) REG_MATCH_NULL_STRING_P(reg_info[reg_no]) = ret; if (!ret) return false; break; /* If this is an optimized succeed_n for zero times, make the jump. */ case jump: EXTRACT_NUMBER_AND_INCR(mcnt, p1); if (mcnt >= 0) p1 += mcnt; else return false; break; case succeed_n: /* Get to the number of times to succeed. */ p1 += 2; EXTRACT_NUMBER_AND_INCR(mcnt, p1); if (mcnt == 0) { p1 -= 4; EXTRACT_NUMBER_AND_INCR(mcnt, p1); p1 += mcnt; } else return false; break; case duplicate: if (!REG_MATCH_NULL_STRING_P(reg_info[*p1])) return false; break; case set_number_at: p1 += 4; default: /* All other opcodes mean we cannot match the empty string. */ return false; } *p = p1; return true; } /* common_op_match_null_string_p */ /* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN bytes; nonzero otherwise. */ static int bcmp_translate(s1, s2, len, translate) const char *s1, *s2; register int len; char *translate; { register const unsigned char *p1 = (const unsigned char *) s1, *p2 = (const unsigned char *) s2; while (len) { if (translate[*p1++] != translate[*p2++]) return 1; len--; } return 0; } /* Entry points for GNU code. */ /* re_compile_pattern is the GNU regular expression compiler: it compiles PATTERN (of length SIZE) and puts the result in BUFP. Returns 0 if the pattern was valid, otherwise an error string. Assumes the `allocated' (and perhaps `buffer') and `translate' fields are set in BUFP on entry. We call regex_compile to do the actual compilation. */ const char *re_compile_pattern(pattern, length, bufp) const char *pattern; size_t length; struct re_pattern_buffer *bufp; { reg_errcode_t ret; /* GNU code is written to assume at least RE_NREGS registers will be set (and at least one extra will be -1). */ bufp->regs_allocated = REGS_UNALLOCATED; /* And GNU code determines whether or not to get register information by passing null for the REGS argument to re_match, etc., not by setting no_sub. */ bufp->no_sub = 0; /* Match anchors at newline. */ bufp->newline_anchor = 1; ret = regex_compile(pattern, length, re_syntax_options, bufp); return re_error_msg[(int) ret]; } /* Entry points compatible with 4.2 BSD regex library. We don't define them if this is an Emacs or POSIX compilation. */ /* POSIX.2 functions. Don't define these for Emacs. */ #if !NO_POSIX_COMPAT /* regcomp takes a regular expression as a string and compiles it. PREG is a regex_t *. We do not expect any fields to be initialized, since POSIX says we shouldn't. Thus, we set `buffer' to the compiled pattern; `used' to the length of the compiled pattern; `syntax' to RE_SYNTAX_POSIX_EXTENDED if the REG_EXTENDED bit in CFLAGS is set; otherwise, to RE_SYNTAX_POSIX_BASIC; `newline_anchor' to REG_NEWLINE being set in CFLAGS; `fastmap' and `fastmap_accurate' to zero; `re_nsub' to the number of subexpressions in PATTERN. PATTERN is the address of the pattern string. CFLAGS is a series of bits which affect compilation. If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we use POSIX basic syntax. If REG_NEWLINE is set, then . and [^...] don't match newline. Also, regexec will try a match beginning after every newline. If REG_ICASE is set, then we considers upper- and lowercase versions of letters to be equivalent when matching. If REG_NOSUB is set, then when PREG is passed to regexec, that routine will report only success or failure, and nothing about the registers. It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for the return codes and their meanings.) */ int regcomp(preg, pattern, cflags) regex_t *preg; const char *pattern; int cflags; { reg_errcode_t ret; reg_syntax_t syntax = (cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; /* regex_compile will allocate the space for the compiled pattern. */ preg->buffer = 0; preg->allocated = 0; preg->used = 0; /* Don't bother to use a fastmap when searching. This simplifies the REG_NEWLINE case: if we used a fastmap, we'd have to put all the characters after newlines into the fastmap. This way, we just try every character. */ preg->fastmap = 0; if (cflags & REG_ICASE) { unsigned i; preg->translate = (char *) malloc(CHAR_SET_SIZE); if (preg->translate == NULL) return (int) REG_ESPACE; /* Map uppercase characters to corresponding lowercase ones. */ for (i = 0; i < CHAR_SET_SIZE; i++) preg->translate[i] = ISUPPER(i) ? tolower(i) : i; } else preg->translate = NULL; /* If REG_NEWLINE is set, newlines are treated differently. */ if (cflags & REG_NEWLINE) { /* REG_NEWLINE implies neither . nor [^...] match newline. */ syntax &= ~RE_DOT_NEWLINE; syntax |= RE_HAT_LISTS_NOT_NEWLINE; /* It also changes the matching behavior. */ preg->newline_anchor = 1; } else preg->newline_anchor = 0; preg->no_sub = !!(cflags & REG_NOSUB); /* POSIX says a null character in the pattern terminates it, so we can use strlen here in compiling the pattern. */ ret = regex_compile(pattern, strlen(pattern), syntax, preg); /* POSIX doesn't distinguish between an unmatched open-group and an unmatched close-group: both are REG_EPAREN. */ if (ret == REG_ERPAREN) ret = REG_EPAREN; return (int) ret; } /* regexec searches for a given pattern, specified by PREG, in the string STRING. If NMATCH is zero or REG_NOSUB was set in the cflags argument to `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at least NMATCH elements, and we set them to the offsets of the corresponding matched substrings. EFLAGS specifies `execution flags' which affect matching: if REG_NOTBOL is set, then ^ does not match at the beginning of the string; if REG_NOTEOL is set, then $ does not match at the end. We return 0 if we find a match and REG_NOMATCH if not. */ int regexec(preg, string, nmatch, pmatch, eflags) const regex_t *preg; const char *string; size_t nmatch; regmatch_t pmatch[]; int eflags; { int ret; struct re_registers regs; regex_t private_preg; int len = strlen(string); boolean want_reg_info = !preg->no_sub && nmatch > 0; private_preg = *preg; private_preg.not_bol = !!(eflags & REG_NOTBOL); private_preg.not_eol = !!(eflags & REG_NOTEOL); /* The user has told us exactly how many registers to return information about, via `nmatch'. We have to pass that on to the matching routines. */ private_preg.regs_allocated = REGS_FIXED; if (want_reg_info) { regs.num_regs = nmatch; regs.start = TALLOC(nmatch, regoff_t); regs.end = TALLOC(nmatch, regoff_t); if (regs.start == NULL || regs.end == NULL) return (int) REG_NOMATCH; } /* Perform the searching operation. */ ret = re_search(&private_preg, string, len, /* start: */ 0, /* range: */ len, want_reg_info ? ®s : (struct re_registers *) 0); /* Copy the register information to the POSIX structure. */ if (want_reg_info) { if (ret >= 0) { unsigned r; for (r = 0; r < nmatch; r++) { pmatch[r].rm_so = regs.start[r]; pmatch[r].rm_eo = regs.end[r]; } } /* If we needed the temporary register info, free the space now. */ free(regs.start); free(regs.end); } /* We want zero return to mean success, unlike `re_search'. */ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; } /* Returns a message corresponding to an error code, ERRCODE, returned from either regcomp or regexec. We don't use PREG here. */ size_t regerror(errcode, preg, errbuf, errbuf_size) int errcode; const regex_t *preg; char *errbuf; size_t errbuf_size; { const char *msg; size_t msg_size; if (errcode < 0 || errcode >= (sizeof(re_error_msg) / sizeof(re_error_msg[0]))) /* Only error codes returned by the rest of the code should be passed to this routine. If we are given anything else, or if other regex code generates an invalid error code, then the program has a bug. Dump core so we can fix it. */ abort(); msg = re_error_msg[errcode]; /* POSIX doesn't require that we do anything in this case, but why not be nice. */ if (!msg) msg = "Success"; msg_size = strlen(msg) + 1; /* Includes the null. */ if (errbuf_size != 0) { if (msg_size > errbuf_size) { strncpy(errbuf, msg, errbuf_size - 1); errbuf[errbuf_size - 1] = 0; } else strcpy(errbuf, msg); } return msg_size; } /* Free dynamically allocated space used by PREG. */ void regfree(preg) regex_t *preg; { if (preg->buffer != NULL) free(preg->buffer); preg->buffer = NULL; preg->allocated = 0; preg->used = 0; if (preg->fastmap != NULL) free(preg->fastmap); preg->fastmap = NULL; preg->fastmap_accurate = 0; if (preg->translate != NULL) free(preg->translate); preg->translate = NULL; } #endif /* !NO_POSIX_COMPAT */ multipath-tools-0.4.9/libmultipath/regex.h000066400000000000000000000202061137574366600206740ustar00rootroot00000000000000/* Definitions for data structures and routines for the regular expression library, version 0.12. Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, 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, 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. */ #ifndef __REGEXP_LIBRARY_H__ #define __REGEXP_LIBRARY_H__ typedef long s_reg_t; typedef unsigned long active_reg_t; typedef unsigned long reg_syntax_t; #define RE_BACKSLASH_ESCAPE_IN_LISTS (1L) #define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) #define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) #define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) #define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) #define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) #define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) #define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) #define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) #define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) #define RE_LIMITED_OPS (RE_INTERVALS << 1) #define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) #define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) #define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) #define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) #define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) #define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) #define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) #define RE_NO_GNU_OPS (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) extern reg_syntax_t re_syntax_options; #define RE_SYNTAX_EMACS 0 #define RE_SYNTAX_AWK \ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL | \ RE_NO_BK_PARENS | RE_NO_BK_REFS | \ RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES | \ RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS | \ RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) #define RE_SYNTAX_GNU_AWK \ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) | \ & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS)) #define RE_SYNTAX_POSIX_AWK \ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | \ RE_INTERVALS | RE_NO_GNU_OPS) #define RE_SYNTAX_GREP \ (RE_BK_PLUS_QM | RE_CHAR_CLASSES | \ RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS | \ RE_NEWLINE_ALT) #define RE_SYNTAX_EGREP \ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS | \ RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE | \ RE_NEWLINE_ALT | RE_NO_BK_PARENS | \ RE_NO_BK_VBAR) #define RE_SYNTAX_POSIX_EGREP \ (RE_SYNTAX_EGREP | RE_INTERVALS | \ RE_NO_BK_BRACES) #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC #define _RE_SYNTAX_POSIX_COMMON \ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | \ RE_DOT_NOT_NULL | RE_INTERVALS | \ RE_NO_EMPTY_RANGES) #define RE_SYNTAX_POSIX_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) #define RE_SYNTAX_POSIX_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS | \ RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES | \ RE_NO_BK_PARENS | RE_NO_BK_VBAR | \ RE_UNMATCHED_RIGHT_PAREN_ORD) #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS | \ RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES | \ RE_NO_BK_PARENS | RE_NO_BK_REFS | \ RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* Maximum number of duplicates an interval can allow */ #define RE_DUP_MAX (0x7fff) /* POSIX 'cflags' bits */ #define REG_EXTENDED 1 #define REG_ICASE (REG_EXTENDED << 1) #define REG_NEWLINE (REG_ICASE << 1) #define REG_NOSUB (REG_NEWLINE << 1) /* POSIX `eflags' bits */ #define REG_NOTBOL 1 #define REG_NOTEOL (1 << 1) /* If any error codes are removed, changed, or added, update the `re_error_msg' table in regex.c. */ typedef enum { REG_NOERROR = 0, /* Success. */ REG_NOMATCH, /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes */ REG_BADPAT, /* Invalid pattern. */ REG_ECOLLATE, /* Not implemented. */ REG_ECTYPE, /* Invalid character class name. */ REG_EESCAPE, /* Trailing backslash. */ REG_ESUBREG, /* Invalid back reference. */ REG_EBRACK, /* Unmatched left bracket. */ REG_EPAREN, /* Parenthesis imbalance. */ REG_EBRACE, /* Unmatched \{. */ REG_BADBR, /* Invalid contents of \{\}. */ REG_ERANGE, /* Invalid range end. */ REG_ESPACE, /* Ran out of memory. */ REG_BADRPT, /* No preceding re for repetition op. */ /* Error codes we've added */ REG_EEND, /* Premature end. */ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; #define REGS_UNALLOCATED 0 #define REGS_REALLOCATE 1 #define REGS_FIXED 2 /* This data structure represents a compiled pattern */ struct re_pattern_buffer { unsigned char *buffer; unsigned long allocated; unsigned long used; reg_syntax_t syntax; char *fastmap; char *translate; size_t re_nsub; unsigned can_be_null : 1; unsigned regs_allocated : 2; unsigned fastmap_accurate : 1; unsigned no_sub : 1; unsigned not_bol : 1; unsigned not_eol : 1; unsigned newline_anchor : 1; }; typedef struct re_pattern_buffer regex_t; /* search.c (search_buffer) in Emacs needs this one opcode value. It is defined both in `regex.c' and here. */ #define RE_EXACTN_VALUE 1 /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; /* This is the structure we store register match data in. See regex.texinfo for a full description of what registers match. */ struct re_registers { unsigned num_regs; regoff_t *start; regoff_t *end; }; #ifndef RE_NREGS #define RE_NREGS 30 #endif /* POSIX specification for registers. Aside from the different names than `re_registers', POSIX uses an array of structures, instead of a structure of arrays. */ typedef struct { regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; /* Declarations for routines. */ extern reg_syntax_t re_set_syntax (reg_syntax_t syntax); extern const char *re_compile_pattern (const char *pattern, size_t length, struct re_pattern_buffer *buffer); extern int re_compile_fastmap (struct re_pattern_buffer *buffer); extern int re_search (struct re_pattern_buffer *buffer, const char *string, int length, int start, int range, struct re_registers *regs); extern int re_search_2 (struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop); extern int re_match (struct re_pattern_buffer *buffer, const char *string, int length, int start, struct re_registers *regs); extern int re_match_2 (struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, struct re_registers *regs, int stop); extern void re_set_registers (struct re_pattern_buffer *buffer, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends); /* 4.2 bsd compatibility. */ extern char *re_comp (const char *); extern int re_exec (const char *); /* POSIX compatibility. */ extern int regcomp (regex_t *preg, const char *pattern, int cflags); extern int regexec (const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); extern size_t regerror (int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); extern void regfree (regex_t *preg); #endif /* not __REGEXP_LIBRARY_H__ */ multipath-tools-0.4.9/libmultipath/sg_include.h000066400000000000000000000026341137574366600217030ustar00rootroot00000000000000#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.4.9/libmultipath/structs.c000066400000000000000000000143151137574366600212700ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2004 Stefan Bader, IBM */ #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 "waiter.h" #include "prio.h" 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 (pp->fd >= 0) close(pp->fd); FREE(pp); } void free_pathvec (vector vec, int free_paths) { int i; struct path * pp; if (!vec) return; if (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, int free_paths) { if (!pgp) return; free_pathvec(pgp->paths, free_paths); FREE(pgp); } void free_pgvec (vector pgvec, int 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; } return mpp; } extern void free_multipath_attributes (struct multipath * mpp) { if (!mpp) return; if (mpp->selector && mpp->selector != conf->selector && (!mpp->mpe || (mpp->mpe && mpp->selector != mpp->mpe->selector)) && (!mpp->hwe || (mpp->hwe && mpp->selector != mpp->hwe->selector))) { FREE(mpp->selector); mpp->selector = NULL; } if (mpp->features && mpp->features != conf->features && (!mpp->hwe || (mpp->hwe && mpp->features != mpp->hwe->features))) { FREE(mpp->features); mpp->features = NULL; } if (mpp->hwhandler && mpp->hwhandler != conf->hwhandler && (!mpp->hwe || (mpp->hwe && mpp->hwhandler != mpp->hwe->hwhandler))) { FREE(mpp->hwhandler); mpp->hwhandler = NULL; } } void free_multipath (struct multipath * mpp, int free_paths) { if (!mpp) return; free_multipath_attributes(mpp); if (mpp->alias && (!mpp->mpe || (mpp->mpe && mpp->alias != mpp->mpe->alias)) && (mpp->wwid && mpp->alias != mpp->wwid)) { FREE(mpp->alias); mpp->alias = NULL; } if (mpp->dmi) { FREE(mpp->dmi); mpp->dmi = NULL; } /* * better own vecs->lock here */ if (mpp->waiter) ((struct event_thread *)mpp->waiter)->mpp = 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, int 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, int 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; } 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_chomp(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_chomp(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; } 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)) mpp->no_path_retry = NO_PATH_RETRY_QUEUE; } multipath-tools-0.4.9/libmultipath/structs.h000066400000000000000000000117651137574366600213030ustar00rootroot00000000000000#ifndef _STRUCTS_H #define _STRUCTS_H #include #define WWID_SIZE 128 #define SERIAL_SIZE 64 #define NODE_NAME_SIZE 19 #define PATH_STR_SIZE 16 #define PARAMS_SIZE 1024 #define FILE_NAME_SIZE 256 #define CALLOUT_MAX_SIZE 128 #define BLK_DEV_SIZE 33 #define PATH_SIZE 512 #define NAME_SIZE 512 #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 #define SCSI_REV_SIZE 5 #define SCSI_STATE_SIZE 9 #define NO_PATH_RETRY_UNDEF 0 #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 enum free_path_switch { 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 }; 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 queue_without_daemon_states { QUE_NO_DAEMON_UNDEF, QUE_NO_DAEMON_OFF, QUE_NO_DAEMON_ON, }; enum pgtimeouts { PGTIMEOUT_UNDEF, PGTIMEOUT_NONE }; enum attribute_bits { ATTR_UID, ATTR_GID, ATTR_MODE, }; enum flush_states { FLUSH_UNDEF, FLUSH_DISABLED, FLUSH_ENABLED, FLUSH_IN_PROGRESS, }; struct scsi_idlun { int dev_id; int host_unique_id; int host_no; }; struct sg_id { int host_no; int channel; int scsi_id; int lun; short h_cmd_per_lun; short d_queue_depth; int unused1; int unused2; }; struct scsi_dev { char dev[FILE_NAME_SIZE]; struct scsi_idlun scsi_id; int host_no; }; struct sysfs_device { struct sysfs_device *parent; /* parent device */ char devpath[PATH_SIZE]; char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ char kernel[NAME_SIZE]; /* device instance name */ char kernel_number[NAME_SIZE]; char driver[NAME_SIZE]; /* device driver name */ }; struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; struct sysfs_device *sysdev; struct scsi_idlun scsi_id; struct sg_id sg_id; 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 failcount; int priority; int pgindex; char * getuid; struct prio * prio; struct checker checker; struct multipath * mpp; int fd; /* 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 pg_timeout; int flush_on_last_del; int attribute_flags; int fast_io_fail; unsigned int dev_loss; uid_t uid; gid_t gid; mode_t mode; unsigned long long size; vector paths; vector pg; char params[PARAMS_SIZE]; char status[PARAMS_SIZE]; struct dm_info * dmi; /* configlet pointers */ char * alias; char * selector; char * features; char * hwhandler; struct mpentry * mpe; struct hwentry * hwe; /* threads */ void * 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; }; struct pathgroup { long id; int status; int priority; int enabled_paths; vector paths; char * selector; }; 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, int free_paths); void free_pathgroup (struct pathgroup * pgp, int free_paths); void free_pgvec (vector pgvec, int free_paths); void free_multipath (struct multipath *, int free_paths); void free_multipath_attributes (struct multipath *); void drop_multipath (vector mpvec, char * wwid, int free_paths); void free_multipathvec (vector mpvec, int free_paths); 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); void setup_feature(struct multipath *, char *); extern char sysfs_path[PATH_SIZE]; #endif /* _STRUCTS_H */ multipath-tools-0.4.9/libmultipath/structs_vec.c000066400000000000000000000264121137574366600221260ustar00rootroot00000000000000#include #include #include #include "checkers.h" #include "vector.h" #include "defaults.h" #include "debug.h" #include "structs.h" #include "structs_vec.h" #include "waiter.h" #include "devmapper.h" #include "dmparser.h" #include "config.h" #include "propsel.h" #include "sysfs.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 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; pathinfo(pp, conf->hwtable, DI_PRIO | DI_CHECKER); } } return 0; } extern void orphan_path (struct path * pp) { pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->getuid = NULL; pp->prio = NULL; 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) { condlog(4, "%s: orphaned", pp->dev); orphan_path(pp); } } } static void set_multipath_wwid (struct multipath * mpp) { if (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) { 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) { if (!mpp) return 1; if (dm_get_map(mpp->alias, &mpp->size, mpp->params)) return 1; if (disassemble_map(pathvec, mpp->params, mpp)) return 1; return 0; } static int update_multipath_status (struct multipath *mpp) { if (!mpp) return 1; if(dm_get_status(mpp->alias, mpp->status)) return 1; if (disassemble_status(mpp->status, mpp)) return 1; return 0; } extern int update_multipath_strings (struct multipath *mpp, vector 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; 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); if (mpp->nr_active > 0) 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) { retry: 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; } set_multipath_wwid(mpp); mpp->mpe = find_mpe(mpp->wwid); condlog(3, "%s: discover", mpp->alias); if (update_multipath_strings(mpp, vecs->pathvec)) { char new_alias[WWID_SIZE]; /* * detect an external rename of the multipath device */ if (dm_get_name(mpp->wwid, new_alias)) { condlog(3, "%s multipath mapped device name has " "changed from %s to %s", mpp->wwid, mpp->alias, new_alias); strcpy(mpp->alias, new_alias); if (mpp->waiter) strncpy(((struct event_thread *)mpp->waiter)->mapname, new_alias, WWID_SIZE); goto retry; } condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } //adopt_paths(vecs->pathvec, mpp); if (!mpp->hwe) mpp->hwe = extract_hwe_from_path(mpp); if (!mpp->hwe) { condlog(3, "%s: no hardware entry found, using defaults", mpp->alias); } select_rr_weight(mpp); select_pgfailback(mpp); set_no_path_retry(mpp); select_pg_timeout(mpp); select_flush_on_last_del(mpp); return 0; out: remove_map(mpp, vecs, PURGE_VEC); return 1; } extern struct multipath * add_map_without_path (struct vectors * vecs, int minor, char * alias) { struct multipath * mpp = alloc_multipath(); if (!mpp) return NULL; mpp->alias = alias; if (setup_multipath(vecs, mpp)) { mpp->alias = NULL; return NULL; /* mpp freed in setup_multipath */ } if (adopt_paths(vecs->pathvec, mpp)) 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; } extern struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec) { struct multipath * mpp; if (!(mpp = alloc_multipath())) return NULL; mpp->mpe = find_mpe(pp->wwid); mpp->hwe = pp->hwe; strcpy(mpp->wwid, pp->wwid); select_alias(mpp); mpp->size = pp->size; if (adopt_paths(vecs->pathvec, mpp)) 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, vector rpvec) { 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 (!pp->sysdev || sysfs_get_dev(pp->sysdev, pp->dev_t, BLK_DEV_SIZE)) { condlog(0, "%s: failed to access path %s", mpp->alias, pp->sysdev ? pp->sysdev->devpath : pp->dev_t); count++; vector_del_slot(mpp->paths, i); i--; if (rpvec) store_path(rpvec, pp); else { 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) { 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\n", mapname); return 2; } free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; if (setup_multipath(vecs, mpp)) 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_t); 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.4.9/libmultipath/structs_vec.h000066400000000000000000000025551137574366600221350ustar00rootroot00000000000000#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); void orphan_paths (vector pathvec, struct multipath * mpp); void orphan_path (struct path * pp); int verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec); int update_mpp_paths(struct multipath * mpp, vector pathvec); int setup_multipath (struct vectors * vecs, struct multipath * mpp); 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, int minor, 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); 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.4.9/libmultipath/switchgroup.c000066400000000000000000000023441137574366600221360ustar00rootroot00000000000000/* * 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_DOWN) { priority += pp->priority; pgp->enabled_paths++; } } pgp->priority = priority; } extern int select_path_group (struct multipath * mpp) { int i; int max_priority = 0, avg_priority; 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) { avg_priority = pgp->priority / pgp->enabled_paths; if (avg_priority > max_priority) { max_priority = avg_priority; max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } else if (avg_priority == max_priority) { if (pgp->enabled_paths > max_enabled_paths) { max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } } } } return bestpg; } multipath-tools-0.4.9/libmultipath/switchgroup.h000066400000000000000000000001461137574366600221410ustar00rootroot00000000000000void path_group_prio_update (struct pathgroup * pgp); int select_path_group (struct multipath * mpp); multipath-tools-0.4.9/libmultipath/sysfs.c000066400000000000000000000410611137574366600207260ustar00rootroot00000000000000/* * 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 "checkers.h" #include "vector.h" #include "structs.h" #include "sysfs.h" #include "list.h" #include "util.h" #include "debug.h" char sysfs_path[PATH_SIZE]; /* attribute value cache */ static LIST_HEAD(attr_list); struct sysfs_attr { struct list_head node; char path[PATH_SIZE]; char *value; /* points to value_local if value is cached */ char value_local[NAME_SIZE]; }; /* list of sysfs devices */ static LIST_HEAD(sysfs_dev_list); struct sysfs_dev { struct list_head node; struct sysfs_device dev; }; int sysfs_init(char *path, size_t len) { if (path) { strlcpy(sysfs_path, path, len); remove_trailing_chars(sysfs_path, '/'); } else strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); dbg("sysfs_path='%s'", sysfs_path); INIT_LIST_HEAD(&attr_list); INIT_LIST_HEAD(&sysfs_dev_list); return 0; } void sysfs_cleanup(void) { struct sysfs_attr *attr_loop; struct sysfs_attr *attr_temp; struct sysfs_dev *sysdev_loop; struct sysfs_dev *sysdev_temp; list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { list_del(&attr_loop->node); free(attr_loop); } list_for_each_entry_safe(sysdev_loop, sysdev_temp, &sysfs_dev_list, node) { list_del(&sysdev_loop->node); free(sysdev_loop); } } void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, const char *subsystem, const char *driver) { char *pos; strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); if (subsystem != NULL) strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); if (driver != NULL) strlcpy(dev->driver, driver, sizeof(dev->driver)); /* set kernel name */ pos = strrchr(dev->devpath, '/'); if (pos == NULL) return; strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); dbg("kernel='%s'", dev->kernel); /* some devices have '!' in their name, change that to '/' */ pos = dev->kernel; while (pos[0] != '\0') { if (pos[0] == '!') pos[0] = '/'; pos++; } /* get kernel number */ pos = &dev->kernel[strlen(dev->kernel)]; while (isdigit(pos[-1])) pos--; strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); dbg("kernel_number='%s'", dev->kernel_number); } int sysfs_resolve_link(char *devpath, size_t size) { char link_path[PATH_SIZE]; char link_target[PATH_SIZE]; int len; int i; int back; strlcpy(link_path, sysfs_path, sizeof(link_path)); strlcat(link_path, devpath, sizeof(link_path)); len = readlink(link_path, link_target, sizeof(link_target)); if (len <= 0) return -1; link_target[len] = '\0'; dbg("path link '%s' points to '%s'", devpath, link_target); for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) ; dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); for (i = 0; i <= back; i++) { char *pos = strrchr(devpath, '/'); if (pos == NULL) return -1; pos[0] = '\0'; } dbg("after moving back '%s'", devpath); strlcat(devpath, "/", size); strlcat(devpath, &link_target[back * 3], size); return 0; } struct sysfs_device *sysfs_device_get(const char *devpath) { char path[PATH_SIZE]; char devpath_real[PATH_SIZE]; struct sysfs_device *dev = NULL; struct sysfs_dev *sysdev_loop, *sysdev; struct stat statbuf; char link_path[PATH_SIZE]; char link_target[PATH_SIZE]; int len; char *pos; /* we handle only these devpathes */ if (devpath != NULL && strncmp(devpath, "/devices/", 9) != 0 && strncmp(devpath, "/subsystem/", 11) != 0 && strncmp(devpath, "/module/", 8) != 0 && strncmp(devpath, "/bus/", 5) != 0 && strncmp(devpath, "/class/", 7) != 0 && strncmp(devpath, "/block/", 7) != 0) { dbg("invalid devpath '%s'", devpath); return NULL; } dbg("open '%s'", devpath); strlcpy(devpath_real, devpath, sizeof(devpath_real)); remove_trailing_chars(devpath_real, '/'); if (devpath[0] == '\0' ) return NULL; /* if we got a link, resolve it to the real device */ strlcpy(path, sysfs_path, sizeof(path)); strlcat(path, devpath_real, sizeof(path)); if (lstat(path, &statbuf) != 0) { /* if stat fails look in the cache */ dbg("stat '%s' failed: %s", path, strerror(errno)); list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { dbg("found vanished dev in cache '%s'", sysdev_loop->dev.devpath); return &sysdev_loop->dev; } } return NULL; } if (S_ISLNK(statbuf.st_mode)) { if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) return NULL; } list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { dbg("found dev in cache '%s'", sysdev_loop->dev.devpath); dev = &sysdev_loop->dev; } } if(!dev) { /* it is a new device */ dbg("new device '%s'", devpath_real); sysdev = malloc(sizeof(struct sysfs_dev)); if (sysdev == NULL) return NULL; memset(sysdev, 0x00, sizeof(struct sysfs_dev)); list_add(&sysdev->node, &sysfs_dev_list); dev = &sysdev->dev; } sysfs_device_set_values(dev, devpath_real, NULL, NULL); /* get subsystem name */ strlcpy(link_path, sysfs_path, sizeof(link_path)); strlcat(link_path, dev->devpath, sizeof(link_path)); strlcat(link_path, "/subsystem", sizeof(link_path)); len = readlink(link_path, link_target, sizeof(link_target)); if (len > 0) { /* get subsystem from "subsystem" link */ link_target[len] = '\0'; dbg("subsystem link '%s' points to '%s'", link_path, link_target); pos = strrchr(link_target, '/'); if (pos != NULL) strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); } else if (strstr(dev->devpath, "/drivers/") != NULL) { strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); } else if (strncmp(dev->devpath, "/module/", 8) == 0) { strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) { pos = strrchr(dev->devpath, '/'); if (pos == &dev->devpath[10]) strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); } else if (strncmp(dev->devpath, "/class/", 7) == 0) { pos = strrchr(dev->devpath, '/'); if (pos == &dev->devpath[6]) strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); } else if (strncmp(dev->devpath, "/bus/", 5) == 0) { pos = strrchr(dev->devpath, '/'); if (pos == &dev->devpath[4]) strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); } /* get driver name */ strlcpy(link_path, sysfs_path, sizeof(link_path)); strlcat(link_path, dev->devpath, sizeof(link_path)); strlcat(link_path, "/driver", sizeof(link_path)); len = readlink(link_path, link_target, sizeof(link_target)); if (len > 0) { link_target[len] = '\0'; dbg("driver link '%s' points to '%s'", link_path, link_target); pos = strrchr(link_target, '/'); if (pos != NULL) strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); } return dev; } struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) { char parent_devpath[PATH_SIZE]; char *pos; dbg("open '%s'", dev->devpath); /* look if we already know the parent */ if (dev->parent != NULL) return dev->parent; strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); dbg("'%s'", parent_devpath); /* strip last element */ pos = strrchr(parent_devpath, '/'); if (pos == NULL || pos == parent_devpath) return NULL; pos[0] = '\0'; if (strncmp(parent_devpath, "/class", 6) == 0) { pos = strrchr(parent_devpath, '/'); if (pos == &parent_devpath[6] || pos == parent_devpath) { dbg("/class top level, look for device link"); goto device_link; } } if (strcmp(parent_devpath, "/block") == 0) { dbg("/block top level, look for device link"); goto device_link; } /* are we at the top level? */ pos = strrchr(parent_devpath, '/'); if (pos == NULL || pos == parent_devpath) return NULL; /* get parent and remember it */ dev->parent = sysfs_device_get(parent_devpath); return dev->parent; device_link: strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); strlcat(parent_devpath, "/device", sizeof(parent_devpath)); if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) return NULL; /* get parent and remember it */ dev->parent = sysfs_device_get(parent_devpath); return dev->parent; } struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) { struct sysfs_device *dev_parent; dev_parent = sysfs_device_get_parent(dev); while (dev_parent != NULL) { if (strcmp(dev_parent->subsystem, subsystem) == 0) return dev_parent; dev_parent = sysfs_device_get_parent(dev_parent); } return NULL; } void sysfs_device_put(struct sysfs_device *dev) { struct sysfs_dev *sysdev_loop; list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { if (&sysdev_loop->dev == dev) { dbg("removed dev '%s' from cache", sysdev_loop->dev.devpath); list_del(&sysdev_loop->node); free(sysdev_loop); return; } } dbg("dev '%s' not found in cache", sysdev_loop->dev.devpath); return; } int sysfs_attr_set_value(const char *devpath, const char *attr_name, const char *value) { char path_full[PATH_SIZE]; int sysfs_len; struct stat statbuf; int fd, value_len, ret = -1; dbg("open '%s'/'%s'", devpath, attr_name); sysfs_len = snprintf(path_full, PATH_SIZE, "%s%s/%s", sysfs_path, devpath, attr_name); if (sysfs_len >= PATH_SIZE || sysfs_len < 0) { if (sysfs_len < 0) dbg("cannot copy sysfs path %s%s/%s : %s", sysfs_path, devpath, attr_name, strerror(errno)); else dbg("sysfs_path %s%s/%s too large", sysfs_path, devpath, attr_name); goto out; } if (stat(path_full, &statbuf) != 0) { dbg("stat '%s' failed: %s" path_full, strerror(errno)); goto out; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) goto out; if ((statbuf.st_mode & S_IWUSR) == 0) goto out; fd = open(path_full, O_WRONLY); if (fd < 0) { dbg("attribute '%s' can not be opened: %s", path_full, strerror(errno)); goto out; } value_len = strlen(value) + 1; ret = write(fd, value, value_len); if (ret == value_len) ret = 0; else if (ret < 0) dbg("write to %s failed: %s", path_full, strerror(errno)); else { dbg("tried to write %d to %s. Wrote %d\n", value_len, path_full, ret); ret = -1; } close(fd); out: return ret; } char *sysfs_attr_get_value(const char *devpath, const char *attr_name) { char path_full[PATH_SIZE]; const char *path; char value[NAME_SIZE]; struct sysfs_attr *attr_loop; struct sysfs_attr *attr = NULL; struct stat statbuf; int fd; ssize_t size; size_t sysfs_len; dbg("open '%s'/'%s'", devpath, attr_name); sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); if(sysfs_len >= sizeof(path_full)) sysfs_len = sizeof(path_full) - 1; path = &path_full[sysfs_len]; strlcat(path_full, devpath, sizeof(path_full)); strlcat(path_full, "/", sizeof(path_full)); strlcat(path_full, attr_name, sizeof(path_full)); /* look for attribute in cache */ list_for_each_entry(attr_loop, &attr_list, node) { if (strcmp(attr_loop->path, path) == 0) { dbg("found in cache '%s'", attr_loop->path); attr = attr_loop; } } if (!attr) { /* store attribute in cache */ dbg("new uncached attribute '%s'", path_full); attr = malloc(sizeof(struct sysfs_attr)); if (attr == NULL) return NULL; memset(attr, 0x00, sizeof(struct sysfs_attr)); strlcpy(attr->path, path, sizeof(attr->path)); dbg("add to cache '%s'", path_full); list_add(&attr->node, &attr_list); } else { /* clear old value */ if(attr->value) memset(attr->value, 0x00, sizeof(attr->value)); } if (lstat(path_full, &statbuf) != 0) { dbg("stat '%s' failed: %s", path_full, strerror(errno)); goto out; } if (S_ISLNK(statbuf.st_mode)) { /* links return the last element of the target path */ char link_target[PATH_SIZE]; int len; const char *pos; len = readlink(path_full, link_target, sizeof(link_target)); if (len > 0) { link_target[len] = '\0'; pos = strrchr(link_target, '/'); if (pos != NULL) { dbg("cache '%s' with link value '%s'", path_full, value); strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); attr->value = attr->value_local; } } goto out; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) goto out; /* skip non-readable files */ if ((statbuf.st_mode & S_IRUSR) == 0) goto out; /* read attribute value */ fd = open(path_full, O_RDONLY); if (fd < 0) { dbg("attribute '%s' can not be opened: %s", path_full, strerror(errno)); goto out; } size = read(fd, value, sizeof(value)); close(fd); if (size < 0) goto out; if (size == sizeof(value)) { dbg("overflow in attribute '%s', truncating", path_full); size--; } /* got a valid value, store and return it */ value[size] = '\0'; remove_trailing_chars(value, '\n'); dbg("cache '%s' with attribute value '%s'", path_full, value); strlcpy(attr->value_local, value, sizeof(attr->value_local)); attr->value = attr->value_local; out: return attr && attr->value && strlen(attr->value) ? attr->value : NULL; } int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len, const char *subsystem, const char *id) { size_t sysfs_len; char path_full[PATH_SIZE]; char *path; struct stat statbuf; sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); path = &path_full[sysfs_len]; if (strcmp(subsystem, "subsystem") == 0) { strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; goto out; strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; } if (strcmp(subsystem, "module") == 0) { strlcpy(path, "/module/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; goto out; } if (strcmp(subsystem, "drivers") == 0) { char subsys[NAME_SIZE]; char *driver; strlcpy(subsys, id, sizeof(subsys)); driver = strchr(subsys, ':'); if (driver != NULL) { driver[0] = '\0'; driver = &driver[1]; strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); strlcat(path, subsys, sizeof(path_full) - sysfs_len); strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); strlcat(path, driver, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); strlcat(path, subsys, sizeof(path_full) - sysfs_len); strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); strlcat(path, driver, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; } goto out; } strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); strlcat(path, subsystem, sizeof(path_full) - sysfs_len); strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); strlcat(path, subsystem, sizeof(path_full) - sysfs_len); strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); strlcat(path, subsystem, sizeof(path_full) - sysfs_len); strlcat(path, "/", sizeof(path_full) - sysfs_len); strlcat(path, id, sizeof(path_full) - sysfs_len); if (stat(path_full, &statbuf) == 0) goto found; out: return 0; found: if (S_ISLNK(statbuf.st_mode)) sysfs_resolve_link(path, sizeof(path_full) - sysfs_len); strlcpy(devpath_full, path, len); return 1; } multipath-tools-0.4.9/libmultipath/sysfs.h000066400000000000000000000017371137574366600207410ustar00rootroot00000000000000/* * sysfs.h */ #ifndef _LIBMULTIPATH_SYSFS_H #define _LIBMULTIPATH_SYSFS_H #ifdef DEBUG # define dbg(format, args...) condlog(4, format, ##args) #else # define dbg(format, args...) do {} while (0) #endif int sysfs_init(char *path, size_t len); void sysfs_cleanup(void); void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, const char *subsystem, const char *driver); struct sysfs_device *sysfs_device_get(const char *devpath); struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); void sysfs_device_put(struct sysfs_device *dev); char *sysfs_attr_get_value(const char *devpath, const char *attr_name); int sysfs_resolve_link(char *path, size_t size); int sysfs_get_size (struct sysfs_device * dev, unsigned long long * size); int sysfs_attr_set_value(const char *devpath, const char *attr_name, const char *value); #endif multipath-tools-0.4.9/libmultipath/uevent.c000066400000000000000000000211111137574366600210570ustar00rootroot00000000000000/* * 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 "memory.h" #include "debug.h" #include "uevent.h" typedef int (uev_trigger)(struct uevent *, void * trigger_data); pthread_t uevq_thr; struct uevent *uevqhp, *uevqtp; pthread_mutex_t uevq_lock, *uevq_lockp = &uevq_lock; pthread_mutex_t uevc_lock, *uevc_lockp = &uevc_lock; pthread_cond_t uev_cond, *uev_condp = &uev_cond; uev_trigger *my_uev_trigger; void * my_trigger_data; int servicing_uev; int is_uevent_busy(void) { return (uevqhp != NULL || servicing_uev); } static struct uevent * alloc_uevent (void) { return (struct uevent *)MALLOC(sizeof(struct uevent)); } void service_uevq(void) { int empty; struct uevent *uev; do { pthread_mutex_lock(uevq_lockp); empty = (uevqhp == NULL); if (!empty) { uev = uevqhp; uevqhp = uev->next; if (uevqtp == uev) uevqtp = uev->next; pthread_mutex_unlock(uevq_lockp); if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data)) condlog(0, "uevent trigger error"); FREE(uev); } else { pthread_mutex_unlock(uevq_lockp); } } while (empty == 0); } /* * Service the uevent queue. */ static void * uevq_thread(void * et) { mlockall(MCL_CURRENT | MCL_FUTURE); while (1) { pthread_mutex_lock(uevc_lockp); servicing_uev = 0; pthread_cond_wait(uev_condp, uevc_lockp); servicing_uev = 1; pthread_mutex_unlock(uevc_lockp); service_uevq(); } return NULL; } int uevent_listen(int (*uev_trigger)(struct uevent *, void * trigger_data), void * trigger_data) { 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; pthread_attr_t attr; const int feature_on = 1; my_uev_trigger = uev_trigger; my_trigger_data = trigger_data; /* * 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. */ uevqhp = uevqtp = NULL; pthread_mutex_init(uevq_lockp, NULL); pthread_mutex_init(uevc_lockp, NULL); pthread_cond_init(uev_condp, NULL); pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 64 * 1024); pthread_create(&uevq_thr, &attr, uevq_thread, NULL); /* * 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) { int i; char *pos; size_t bufpos; ssize_t buflen; struct uevent *uev; char *buffer; 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"); 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; } uev = alloc_uevent(); if (!uev) { condlog(1, "lost uevent, oom"); continue; } 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); continue; } 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; bufpos += keylen + 1; } uev->envp[i] = NULL; condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath); /* print payload environment */ for (i = 0; uev->envp[i] != NULL; i++) condlog(3, "%s", uev->envp[i]); /* * Queue uevent and poke service pthread. */ pthread_mutex_lock(uevq_lockp); if (uevqtp) uevqtp->next = uev; else uevqhp = uev; uevqtp = uev; uev->next = NULL; pthread_mutex_unlock(uevq_lockp); pthread_mutex_lock(uevc_lockp); pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevc_lockp); } exit: close(sock); pthread_mutex_lock(uevq_lockp); pthread_cancel(uevq_thr); pthread_mutex_unlock(uevq_lockp); pthread_mutex_destroy(uevq_lockp); pthread_mutex_destroy(uevc_lockp); pthread_cond_destroy(uev_condp); return 1; } multipath-tools-0.4.9/libmultipath/uevent.h000066400000000000000000000010041137574366600210630ustar00rootroot00000000000000/* environment buffer, the kernel's size in lib/kobject_uevent.c should fit in */ #define HOTPLUG_BUFFER_SIZE 1024 #define HOTPLUG_NUM_ENVP 32 #define OBJECT_SIZE 512 #ifndef NETLINK_KOBJECT_UEVENT #define NETLINK_KOBJECT_UEVENT 15 #endif struct uevent { void *next; char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE]; char *devpath; char *action; char *envp[HOTPLUG_NUM_ENVP]; }; int uevent_listen(int (*store_uev)(struct uevent *, void * trigger_data), void * trigger_data); int is_uevent_busy(void); multipath-tools-0.4.9/libmultipath/util.c000066400000000000000000000043331137574366600205350ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "memory.h" #define PARAMS_SIZE 255 int strcmp_chomp(char *str1, char *str2) { int i; char s1[PARAMS_SIZE],s2[PARAMS_SIZE]; if(!str1 || !str2) return 1; strncpy(s1, str1, PARAMS_SIZE); strncpy(s2, str2, PARAMS_SIZE); for (i=strlen(s1)-1; i >=0 && isspace(s1[i]); --i) ; s1[++i] = '\0'; for (i=strlen(s2)-1; i >=0 && isspace(s2[i]); --i) ; s2[++i] = '\0'; return(strcmp(s1,s2)); } void strchop(char *str) { int i; for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ; str[++i] = '\0'; } void basenamecpy (char * str1, char * str2) { char *p = str1 + (strlen(str1) - 1); while (*--p != '/' && p != str1) continue; if (p != str1) p++; strcpy(str2, p); } 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\n"); 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; } void remove_trailing_chars(char *path, char c) { size_t len; len = strlen(path); while (len > 0 && path[len-1] == c) path[--len] = '\0'; } multipath-tools-0.4.9/libmultipath/util.h000066400000000000000000000011271137574366600205400ustar00rootroot00000000000000#ifndef _UTIL_H #define _UTIL_H int strcmp_chomp(char *, char *); void strchop(char *); void basenamecpy (char * src, char * dst); 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); void remove_trailing_chars(char *path, char c); #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.4.9/libmultipath/uxsock.c000066400000000000000000000055551137574366600211030ustar00rootroot00000000000000/* * 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 #include "memory.h" #include "uxsock.h" /* * connect to a unix domain socket */ int ux_socket_connect(const char *name) { int fd; struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, name, sizeof(addr.sun_path)); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { return -1; } if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 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; struct sockaddr_un addr; /* get rid of any old socket */ unlink(name); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) return -1; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, name, sizeof(addr.sun_path)); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { close(fd); return -1; } if (listen(fd, 10) == -1) { 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 */ size_t read_all(int fd, void *buf, size_t len) { size_t total = 0; while (len) { ssize_t n = read(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; } /* * 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) { if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1; (*buf) = MALLOC(*len); if (!*buf) return -1; if (read_all(fd, *buf, *len) != *len) { FREE(*buf); return -1; } return 0; } multipath-tools-0.4.9/libmultipath/uxsock.h000066400000000000000000000004661137574366600211040ustar00rootroot00000000000000/* 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); size_t write_all(int fd, const void *buf, size_t len); size_t read_all(int fd, void *buf, size_t len); multipath-tools-0.4.9/libmultipath/vector.c000066400000000000000000000055221137574366600210630ustar00rootroot00000000000000/* * 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) { v->allocated += VECTOR_DEFAULT_SIZE; if (v->slot) v->slot = REALLOC(v->slot, sizeof (void *) * v->allocated); else v->slot = (void *) MALLOC(sizeof (void *) * v->allocated); if (!v->slot) v->allocated -= VECTOR_DEFAULT_SIZE; return v->slot; } void * vector_insert_slot(vector v, int slot, void *value) { int i; if (!vector_alloc_slot(v)) return NULL; for (i = (v->allocated /VECTOR_DEFAULT_SIZE) - 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; for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++) if (v->slot[i] == addr) return i; return -1; } void vector_del_slot(vector v, int slot) { int i; if (!v->allocated || slot < 0 || slot > VECTOR_SIZE(v)) return; for (i = slot + 1; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++) v->slot[i-1] = v->slot[i]; v->allocated -= VECTOR_DEFAULT_SIZE; if (!v->allocated) { FREE(v->slot); v->slot = NULL; } else v = REALLOC(v->slot, sizeof (void *) * v->allocated); } void vector_repack(vector v) { int i; if (!v->allocated) return; for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); 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); 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 = v->allocated - 1; v->slot[i] = value; } multipath-tools-0.4.9/libmultipath/vector.h000066400000000000000000000041151137574366600210650ustar00rootroot00000000000000/* * 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 { unsigned int allocated; void **slot; }; typedef struct _vector *vector; #define VECTOR_DEFAULT_SIZE 1 #define VECTOR_SLOT(V,E) (((V) && (E) < (V)->allocated) ? (V)->slot[(E)] : NULL) #define VECTOR_SIZE(V) ((V) ? (V)->allocated : 0) #define VECTOR_LAST_SLOT(V) (((V) && (V)->allocated) ? (V)->slot[((V)->allocated - 1)] : NULL) #define vector_foreach_slot(v,p,i) \ for (i = 0; (v) && i < (v)->allocated && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_after(v,p,i) \ for (; (v) && i < (v)->allocated && ((p) = (v)->slot[i]); 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); #endif multipath-tools-0.4.9/libmultipath/version.h000066400000000000000000000023621137574366600212520ustar00rootroot00000000000000/* * 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 0x000409 #define DATE_CODE 0x052110 #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.4.9/libmultipath/waiter.c000066400000000000000000000115451137574366600210560ustar00rootroot00000000000000/* * 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)); return wp; } void free_waiter (void *data) { sigset_t old; struct event_thread *wp = (struct event_thread *)data; /* * indicate in mpp that the wp is already freed storage */ block_signal(SIGHUP, &old); lock(wp->vecs->lock); if (wp->mpp) /* * be careful, mpp may already be freed -- null if so */ wp->mpp->waiter = NULL; else /* * This is OK condition during shutdown. */ condlog(3, "free_waiter, mpp freed before wp=%p (%s).", wp, wp->mapname); unlock(wp->vecs->lock); pthread_sigmask(SIG_SETMASK, &old, NULL); if (wp->dmt) dm_task_destroy(wp->dmt); FREE(wp); } void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs) { struct event_thread *wp = (struct event_thread *)mpp->waiter; pthread_t thread; if (!wp) { condlog(3, "%s: no waiter thread", mpp->alias); return; } thread = wp->thread; condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread); pthread_kill(thread, SIGUSR1); } static sigset_t unblock_signals(void) { sigset_t set, old; sigemptyset(&set); sigaddset(&set, SIGHUP); sigaddset(&set, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &set, &old); return old; } /* * returns the reschedule delay * negative means *stop* */ int waiteventloop (struct event_thread *waiter) { sigset_t set; 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); 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); return 1; } dm_task_no_open_count(waiter->dmt); /* accept wait interruption */ set = unblock_signals(); /* wait */ r = dm_task_run(waiter->dmt); /* wait is over : event or interrupt */ pthread_sigmask(SIG_SETMASK, &set, NULL); if (!r) /* wait interrupted by signal */ return -1; dm_task_destroy(waiter->dmt); waiter->dmt = NULL; 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); r = update_multipath(waiter->vecs, waiter->mapname); 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); block_signal(SIGUSR1, NULL); block_signal(SIGHUP, NULL); 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; mpp->waiter = (void *)wp; strncpy(wp->mapname, mpp->alias, WWID_SIZE); wp->vecs = vecs; wp->mpp = mpp; if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { condlog(0, "%s: cannot create event checker", wp->mapname); goto out1; } condlog(2, "%s: event checker started", wp->mapname); return 0; out1: free_waiter(wp); mpp->waiter = NULL; out: condlog(0, "failed to start waiter thread"); return 1; } multipath-tools-0.4.9/libmultipath/waiter.h000066400000000000000000000010411137574366600210510ustar00rootroot00000000000000#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 multipath *mpp; }; 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.4.9/multipath-tools.spec.in000066400000000000000000000030211137574366600213350ustar00rootroot00000000000000%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.4.9/multipath.conf.annotated000066400000000000000000000427761137574366600215650ustar00rootroot00000000000000## ## This is a template multipath-tools configuration file ## Uncomment the lines relevent to your environment ## # ## ## name : defaults ## desc : multipath-tools default settings ## #defaults { # # # # name : udev_dir # # desc : directory where udev creates its device nodes # # default : /dev # # # udev_dir /dev # # # # # 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 : 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" # # default : "round-robin 0" # # # selector "round-robin 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 : getuid_callout # # scope : multipath & multipathd # # desc : the default program and args to callout to obtain a unique # # path identifier. Absolute path required # # 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 : (null) # # # 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. The # # only existing feature currently is queue_if_no_path, which # # is the same as setting no_path_retry to queue. # # values : "1 queue_if_no_path" # # default : (null) # # # features "1 queue_if_no_path" # # # # # 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 # # default : 1000 # # # 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 : 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 : 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 : n > 0 # # default : determined by the OS # dev_loss_tmo 600 #} # ## ## 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 # # } # 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) ## #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 alorithm to use to check path state # # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac| # # cciss_tur # # # path_checker directio # # # # # name : path_selector # # scope : multipathd & 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 : features # # scope : multipath & multipathd # # desc : The extra features of multipath devices. The only # # existing feature currently is queue_if_no_path, # # which is the same as setting no_path_retry to queue. # # values : "1 queue_if_no_path" # # # features "1 queue_if_no_path" # # # # # 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 # } # device { # vendor "COMPAQ " # product "MSA1000 " # path_grouping_policy multibus # path_checker tur # rr_weight priorities # } #} multipath-tools-0.4.9/multipath.conf.defaults000066400000000000000000000412361137574366600214050ustar00rootroot00000000000000# These are the compiled in default settings. They will be used unless you # overwrite these values in your config file. #defaults { # udev_dir /dev # polling_interval 5 # selector "round-robin 0" # path_grouping_policy failover # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # prio const # prio_args "" # path_checker directio # rr_min_io 1000 # rr_weight uniform # failback manual # no_path_retry fail # user_friendly_names no #} # #blacklist { # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^hd[a-z]" # devnode "^dcssblk[0-9]*" #} # #devices { # device { # vendor "APPLE*" # product "Xserve RAID" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "3PARdata" # product "VV" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "DEC" # product "HSG80" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "1 hp_sw" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker hp_sw # prio hp_sw # prio_args "" # } # device { # vendor "HP" # product "A6189A" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "(COMPAQ|HP)" # product "(MSA|HSV)1.0.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "1 hp_sw" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker hp_sw # prio hp_sw # prio_args "" # } # device { # vendor "(COMPAQ|HP)" # product "MSA VOLUME" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker tur # prio alua # prio_args "" # } # device { # vendor "HP" # product "MSA2000s*" # getuid_callout "/sbin/cciss_id %n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "(COMPAQ|HP)" # product "HSV1[01]1|HSV2[01]0|HSV300|HSV4[05]0" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker tur # prio alua # prio_args "" # } # device { # vendor "HP" # product "MSA2[02]12*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # failback immediate # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "HP" # product "LOGICAL VOLUME.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # failback immediate # rr_weight uniform # no_path_retry 12 # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "DDN" # product "SAN DataDirector" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "EMC" # product "SYMMETRIX" # getuid_callout "/lib/udev/scsi_id --whitelisted --page=pre-spc3-83 --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "DGC" # product ".*" # product_blacklist "LUNZ" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "1 emc" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry 60 # rr_min_io 1000 # path_checker emc_clariion # prio emc # prio_args "" # } # device { # vendor "EMC" # product "Invista" # product_blacklist "LUNZ" # getuid_callout "/sbin/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy failover # rr_weight uniform # no_path_retry 5 # rr_min_io 1000 # path_checker tur # } # device { # vendor "EMC" # product "Invista" # product_blacklist "LUNZ" # getuid_callout "/lib/udev/scsi_id --whitelisted --page=pre-spc3-83 --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # no_path_retry 5 # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "FSC" # product "CentricStor" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_serial # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "(HITACHI|HP)" # product "OPEN-.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 100 # path_checker tur # prio const # prio_args "" # } # device { # vendor "HITACHI" # product "DF.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio hds # prio_args "" # } # device { # vendor "IBM" # product "ProFibre 4000R" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker readsector0 # prio const # prio_args "" # } # device { # vendor "IBM" # product "1722-600" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry 300 # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "1742" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "1745|1746" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "2 pg_init_retries 50" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "1814" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "1815" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "3526" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "IBM" # product "3542" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_serial # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "IBM" # product "2105(800|F20)" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_serial # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "IBM" # product "1750500" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio alua # prio_args "" # } # device { # vendor "IBM" # product "2107900" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "IBM" # product "2145" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio alua # prio_args "" # } # device { # vendor "IBM" # product "S/390 DASD ECKD" # product_blacklist "S/390.*" # getuid_callout "/sbin/dasd_id /dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "NETAPP" # product "LUN.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 128 # path_checker directio # prio netapp # prio_args "" # } # device { # vendor "NEXENTA" # product "COMSTAR" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_serial # failback immediate # rr_weight uniform # no_path_retry 30 # rr_min_io 128 # path_checker directio # prio const # prio_args "" # } # device { # vendor "IBM" # product "Nseries.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 128 # path_checker directio # prio netapp # prio_args "" # } # device { # vendor "Pillar" # product "Axiom.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio alua # prio_args "" # } # device { # vendor "SGI" # product "TP9[13]00" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "SGI" # product "TP9[45]00" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "SGI" # product "IS.*" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "STK" # product "OPENstorage D280" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio rdac # prio_args "" # } # device { # vendor "SUN" # product "(StorEdge 3510|T4)" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker directio # prio const # prio_args "" # } # device { # vendor "PIVOT3" # product "RAIGE VOLUME" # getuid_callout "/lib/udev/scsi_id --whitelisted --page=0x80 --device=/dev/%n" # features "1 queue_if_no_path" # hardware_handler "0" # path_selector "round-robin 0" # path_grouping_policy multibus # rr_weight uniform # rr_min_io 1000 # path_checker tur # prio const # prio_args "" # } # device { # vendor "SUN" # product "CSM200_R" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "SUN" # product "LCSM100_[IF]" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } # device { # vendor "STK" # product "FLEXLINE 380" # product_blacklist "Universal Xport" # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" # features "0" # hardware_handler "1 rdac" # path_selector "round-robin 0" # path_grouping_policy group_by_prio # failback immediate # rr_weight uniform # no_path_retry queue # rr_min_io 1000 # path_checker rdac # prio rdac # prio_args "" # } #} multipath-tools-0.4.9/multipath.conf.synthetic000066400000000000000000000031571137574366600216100ustar00rootroot00000000000000## ## This is a template multipath-tools configuration file ## Uncomment the lines relevent to your environment ## #defaults { # udev_dir /dev # polling_interval 10 # 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 # } #} multipath-tools-0.4.9/multipath/000077500000000000000000000000001137574366600167225ustar00rootroot00000000000000multipath-tools-0.4.9/multipath/01_udev000077500000000000000000000015451137574366600201200ustar00rootroot00000000000000#!/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.4.9/multipath/02_multipath000077500000000000000000000013071137574366600211610ustar00rootroot00000000000000#!/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.4.9/multipath/Makefile000066400000000000000000000020361137574366600203630ustar00rootroot00000000000000# Makefile # # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc OBJS = main.o CFLAGS += -I$(multipathdir) LDFLAGS += -lpthread -ldevmapper -ldl -lmultipath -L$(multipathdir) 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)/etc/udev/rules.d $(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ $(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)/etc/udev/rules.d/multipath.rules rm $(DESTDIR)$(bindir)/$(EXEC) rm $(DESTDIR)$(mandir)/$(EXEC).8.gz rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.4.9/multipath/dev_t.h000066400000000000000000000012071137574366600201740ustar00rootroot00000000000000#define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) #define print_dev_t(buffer, dev) \ sprintf((buffer), "%u:%u\n", MAJOR(dev), MINOR(dev)) #define format_dev_t(buffer, dev) \ ({ \ sprintf(buffer, "%u:%u", MAJOR(dev), MINOR(dev)); \ buffer; \ }) multipath-tools-0.4.9/multipath/main.c000066400000000000000000000246731137574366600200260ustar00rootroot00000000000000/* * 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 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 [-d] [-r] [-v lvl] [-p pol] [-b fil] [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 -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" \ " -d dry run, do not create or update devmaps\n" \ " -r force devmap reload\n" \ " -p policy failover|multibus|group_by_serial|group_by_prio\n" \ " -b fil bindings file location\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" \ ); exit(1); } 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, 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; } static int get_dm_mpvec (vector curmp, vector pathvec, char * refwwid) { int i; struct multipath * mpp; 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; } condlog(3, "params = %s", mpp->params); condlog(3, "status = %s", mpp->status); disassemble_map(pathvec, mpp->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->list != 1) update_paths(mpp); if (conf->list > 1) mpp->bestpg = select_path_group(mpp); disassemble_status(mpp->status, mpp); if (conf->list) print_multipath_topology(mpp, conf->verbosity); if (!conf->dry_run) 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 is "/dev/" . "sysfs block dev" */ if (conf->dev) { if (!strncmp(conf->dev, "/dev/", 5) && strlen(conf->dev) > 5) dev = conf->dev + 5; else dev = conf->dev; } /* * if we have a blacklisted device parameter, exit early */ if (dev && (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) goto out; /* * scope limiting must be translated into a wwid * failing the translation is fatal (by policy) */ if (conf->dev) { refwwid = get_refwwid(conf->dev, conf->dev_type, pathvec); if (!refwwid) { condlog(3, "scope is nul"); goto out; } condlog(3, "scope limited to %s", refwwid); if (filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid) > 0) goto out; } /* * get a path list */ if (conf->dev) di_flag = DI_WWID; if (conf->list > 1) /* extended path info '-ll' */ di_flag |= DI_SYSFS | DI_CHECKER; else if (conf->list) /* minimum path info '-l' */ di_flag |= DI_SYSFS; else /* maximum info */ di_flag = DI_ALL; if (path_discovery(pathvec, conf, di_flag)) 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->list) { r = 0; goto out; } /* * core logic entry point */ r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload); out: if (refwwid) FREE(refwwid); free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); return r; } int main (int argc, char *argv[]) { int arg; extern char *optarg; extern int optind; int i, r = 1; if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } if (dm_prereq()) exit(1); if (load_config(DEFAULT_CONFIGFILE)) exit(1); if (init_checkers()) { condlog(0, "failed to initialize checkers"); exit(1); } if (init_prio()) { condlog(0, "failed to initialize prioritizers"); exit(1); } if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { condlog(0, "multipath tools need sysfs mounted"); exit(1); } while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:r")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; case 'v': if (sizeof(optarg) > sizeof(char *) || !isdigit(optarg[0])) usage (argv[0]); conf->verbosity = atoi(optarg); break; case 'b': conf->bindings_file = optarg; break; case 'd': conf->dry_run = 1; break; case 'f': conf->remove = FLUSH_ONE; break; case 'F': conf->remove = FLUSH_ALL; break; case 'l': conf->list = 1; conf->dry_run = 1; if (optarg && !strncmp(optarg, "l", 1)) conf->list++; 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]); } break; case 'r': conf->force_reload = 1; break; case 'h': usage(argv[0]); case ':': fprintf(stderr, "Missing option arguement\n"); usage(argv[0]); case '?': fprintf(stderr, "Unknown switch: %s\n", optarg); usage(argv[0]); default: usage(argv[0]); } } if (optind < argc) { conf->dev = MALLOC(FILE_NAME_SIZE); if (!conf->dev) goto out; strncpy(conf->dev, argv[optind], FILE_NAME_SIZE); if (filepresent(conf->dev)) conf->dev_type = DEV_DEVNODE; else if (sscanf(conf->dev, "%d:%d", &i, &i) == 2) conf->dev_type = DEV_DEVT; else conf->dev_type = DEV_DEVMAP; } conf->daemon = 0; 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\n", conf->max_fds, strerror(errno)); } dm_init(); if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) r = dm_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_udev_wait(conf->cookie); sysfs_cleanup(); dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); /* * 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; #ifdef _DEBUG_ dbg_free_final(NULL); #endif return r; } multipath-tools-0.4.9/multipath/multipath.8000066400000000000000000000041461137574366600210270ustar00rootroot00000000000000.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 [\| \-d \|] .RB [\| \-h | \-l | \-ll | \-f | \-F \|] .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 multiple paths to devices for fail-over or performance reasons and coalesces them .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 .BI \-p " policy" force maps to 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. .RE .TP .BI device update only the devmap the path pointed by .I device is in. .I device is in the /dev/sdb (as shown by udev in the $DEVNAME variable) or major:minor format. .I device may alternatively be a multipath mapname .SH "SEE ALSO" .BR udev (8), .BR dmsetup (8) .BR hotplug (8) .SH AUTHORS .B multipath was developed by Christophe Varoqui, and others. multipath-tools-0.4.9/multipath/multipath.conf.5000066400000000000000000000243441137574366600217520ustar00rootroot00000000000000.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 occor. .LP The following \fIsection\fP keywords are recognized: .TP 17 .B defaults This section defines default values for attributes which are used whenever no specific setting is given. .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), which is the result of the \fIgetuid_callout\fR program. .TP .B devices This section defines the device-specific settings. .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 (4 * polling_interval); default is .I 5 .TP .B udev_dir directory where udev creates its device nodes; default is .I /dev .TP .B verbosity default verbosity. Higher values increase the verbosity level. Valid levels are between 0 and 6; default is .I 2 .TP .B selector The default path selector algorithm to use; they are offered by the kernel multipath target. The only currently implemented is .I "round-robin 0" .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 \fImultibus\fR. .RE .TP .B getuid_callout The default program and args to callout to obtain a unique path identifier. Should be specified with an absolute path. Default value is .I /lib/udev/scsi_id --whitelisted --device=/dev/%n .TP .B prio_callout The default program and args to callout to obtain a path priority value. The specified program will be executed and should return a numeric value specifying the relative priority of this path. Higher number have a higher priority. A '%n' in the command line will be expanded to the device name, a '%b' will be expanded to the device number in .I major:minor format. .I "none" is a valid value. Currently the following path priority programs are implemented: .RS .TP 12 .B mpath_prio_emc /dev/%n Generate the path priority for EMC arrays .TP .B mpath_prio_alua /dev/%n Generate the path priority based on the SCSI-3 ALUA settings. .TP .B mpath_prio_netapp /dev/%n Generate the path priority for NetApp arrays. .TP .B mpath_prio_rdac /dev/%n Generate the path priority for LSI/Engenio RDAC controller. .TP .B mpath_prio_hp_sw /dev/%n Generate the path priority for Compaq/HP controller in active/standby mode. .TP .B mpath_prio_hds_modular %b Generate the path priority for Hitachi HDS Modular storage arrays. .TP Default value is \fBnone\fR. .RE .TP .B features Specify any device-mapper features to be used. The most common of these features is .I "1 queue_if_no_path" Note that this can also be set via the .I no_path_retry keyword. .TP .B path_checker The default method used to determine the paths' state. Possible values are .RS .TP 12 .B readsector0 Read the first sector of the device .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 RDAC storage controller. .TP .B directio Read the first sector with direct I/O. .TP Default value is \fIreadsector0\fR. .RE .TP .B failback Tell the daemon to manage path group failback, or not to. 0 or .I immediate means immediate failback, values >0 means deferred failback (in seconds). .I manual means no failback. Default value is .I manual .TP .B rr_min_io The number of IO to route to a path before switching to the next in the same path group. Default is .I 1000 .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. Default is 0. .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 overriden by any specific aliases in the \fImultipaths\fR section. 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 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. .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 . .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 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 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 devnode Regular expression of the device nodes 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 "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 section: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B path_selector .TP .B failback .TP .B no_path_retry .TP .B rr_min_io .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 product_blacklist Product strings to blacklist for this vendor .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. .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 getuid_callout .TP .B path_selector .TP .B path_checker .TP .B features .TP .B prio_callout .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B fast_io_fail_tmo .TP .B dev_loss_tmo .RE .PD .LP .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. .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.4.9/multipath/multipath.rules000066400000000000000000000002661137574366600220110ustar00rootroot00000000000000# # udev rules for multipathing. # The persistent symlinks are created with the kpartx rules # # socket for uevents SUBSYSTEM=="block", RUN+="socket:/org/kernel/dm/multipath_event" multipath-tools-0.4.9/multipathd/000077500000000000000000000000001137574366600170665ustar00rootroot00000000000000multipath-tools-0.4.9/multipathd/Makefile000066400000000000000000000016071137574366600205320ustar00rootroot00000000000000EXEC = multipathd include ../Makefile.inc # # basic flags setting # CFLAGS += -I$(multipathdir) LDFLAGS += -lpthread -ldevmapper -lreadline -lncurses -ldl \ -lmultipath -L$(multipathdir) # # 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) $(LDFLAGS) -o $(EXEC) $(OBJS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(rcdir) $(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 clean: rm -f core *.o $(EXEC) *.gz multipath-tools-0.4.9/multipathd/cli.c000066400000000000000000000235261137574366600200110ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include "cli.h" 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, int 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 (int 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 (int fp) { int i; struct handler *h; vector_foreach_slot (handlers, h, i) if (h->fingerprint == fp) return h; return NULL; } int set_handler_callback (int 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 (vector vec) { int i; struct handler * h; vector_foreach_slot (vec, h, i) FREE(h); vector_free(vec); } 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, "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, "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, "format", FMT, 1); r += add_key(keys, "wildcards", WILDCARDS, 0); r += add_key(keys, "quit", QUIT, 0); r += add_key(keys, "exit", QUIT, 0); 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 0; cmdvec = vector_alloc(); *v = cmdvec; 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; } free_strvec(strvec); return 0; out: free_strvec(strvec); free_keys(cmdvec); *v = NULL; return r; } static int fingerprint(vector vec) { int i; int 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, vector keys, struct key * refkw) { int i, fwd = 0; struct key * kw; vector_foreach_slot (keys, kw, i) if (kw->code == refkw->code && kw != refkw) fwd += sprintf(reply, "|%s", kw->str); return fwd; } static char * genhelp_handler (void) { int i, j; int fp; struct handler * h; struct key * kw; char * reply; char * p; reply = MALLOC(INITIAL_REPLY_LEN); if (!reply) return NULL; p = reply; p += sprintf(p, VERSION_STRING); p += sprintf(p, "CLI commands reference:\n"); vector_foreach_slot (handlers, h, i) { fp = h->fingerprint; vector_foreach_slot (keys, kw, j) { if ((kw->code & fp)) { fp -= kw->code; p += sprintf(p, " %s", kw->str); p += genhelp_sprint_aliases(p, keys, kw); if (kw->has_param) p += sprintf(p, " $%s", kw->str); } } p += sprintf(p, "\n"); } return reply; } int parse_cmd (char * cmd, char ** reply, int * len, void * data) { int r; struct handler * h; vector cmdvec; r = get_cmdvec(cmd, &cmdvec); if (r) { if (cmdvec) free_keys(cmdvec); *reply = genhelp_handler(); *len = strlen(*reply) + 1; return 0; } h = find_handler(fingerprint(cmdvec)); if (!h) { *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, int 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+STATUS, 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+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(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); return 0; } static int key_match_fingerprint (struct key * kw, int 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, rlfp, has_param; struct key * kw; int i; struct handler *h; vector v; 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. */ int 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.4.9/multipathd/cli.h000066400000000000000000000034221137574366600200070ustar00rootroot00000000000000enum { __LIST, __ADD, __DEL, __SWITCH, __SUSPEND, __RESUME, __REINSTATE, __FAIL, __RESIZE, __DISABLEQ, __RESTOREQ, __PATHS, __MAPS, __PATH, __MAP, __GROUP, __RECONFIGURE, __STATUS, __STATS, __TOPOLOGY, __CONFIG, __BLACKLIST, __DEVICES, __FMT, __WILDCARDS, __QUIT, }; #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 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 STATUS (1 << __STATUS) #define STATS (1 << __STATS) #define TOPOLOGY (1 << __TOPOLOGY) #define CONFIG (1 << __CONFIG) #define BLACKLIST (1 << __BLACKLIST) #define DEVICES (1 << __DEVICES) #define FMT (1 << __FMT) #define COUNT (1 << __COUNT) #define WILDCARDS (1 << __WILDCARDS) #define QUIT (1 << __QUIT) #define INITIAL_REPLY_LEN 1000 struct key { char * str; char * param; int code; int has_param; }; struct handler { unsigned long fingerprint; int (*fn)(void *, char **, int *, void *); }; vector keys; vector handlers; int alloc_handlers (void); int add_handler (int fp, int (*fn)(void *, char **, int *, void *)); int set_handler_callback (int 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, int code); void free_keys (vector vec); void free_handlers (vector vec); int cli_init (void); char * key_generator (const char * str, int state); multipath-tools-0.4.9/multipathd/cli_handlers.c000066400000000000000000000370051137574366600216660ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "cli.h" int show_paths (char ** r, int * len, struct vectors * vecs, char * style) { 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 (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); again = ((c - reply) == (maxlen - 1)); if (again) reply = REALLOC(reply, maxlen *= 2); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_map_topology (char ** r, int * len, struct multipath * mpp) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 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)); if (again) reply = REALLOC(reply, maxlen *= 2); } *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) c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); again = ((c - reply) == (maxlen - 1)); if (again) reply = REALLOC(reply, maxlen *= 2); } *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; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = 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_mptable(c, reply + maxlen - c, conf->mptable); again = ((c - reply) == maxlen); if (again) reply = REALLOC(reply, maxlen *= 2); } *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); } 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); } 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); 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); } 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_maps (char ** r, int *len, struct vectors * vecs, char * style) { 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 (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); again = ((c - reply) == (maxlen - 1)); if (again) reply = REALLOC(reply, maxlen *= 2); } *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); } 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); } 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); } 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); } int cli_add_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); int r; condlog(2, "%s: add path (operator)", param); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, param) > 0 || (r = ev_add_path(param, vecs)) == 2) { *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: path blacklisted", param); return 0; } return r; } int cli_del_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); condlog(2, "%s: remove path (operator)", param); return ev_remove_path(param, 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 minor; char dev_path[PATH_SIZE]; struct sysfs_device *sysdev; condlog(2, "%s: add map (operator)", param); if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) { *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 0; } minor = dm_get_minor(param); if (minor < 0) { condlog(2, "%s: not a device mapper table", param); return 0; } sprintf(dev_path,"/block/dm-%d", minor); sysdev = sysfs_device_get(dev_path); if (!sysdev) { condlog(2, "%s: not found in sysfs", param); return 0; } return ev_add_map(sysdev, vecs); } int cli_del_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); condlog(2, "%s: remove map (operator)", param); return ev_remove_map(param, vecs); } int resize_map(struct multipath *mpp, unsigned long long size, struct vectors * vecs) { mpp->size = size; update_mpp_paths(mpp, vecs->pathvec); setup_map(mpp); mpp->action = ACT_RESIZE; if (domap(mpp) <= 0) { condlog(0, "%s: failed to resize map : %s", mpp->alias, strerror(errno)); 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; 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); pp = VECTOR_SLOT(pgp->paths, 0); if (sysfs_get_size(pp->sysdev, &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(); setup_multipath(vecs, mpp); sync_map_state(mpp); 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; 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; 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)); 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); 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); 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; 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_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; 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; while (again) { reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprint_blacklist_report(c, maxlen); again = ((c - reply) == maxlen); if (again) { maxlen *= 2; FREE(reply); continue; } } *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; while (again) { reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprint_devices(c, maxlen, vecs); again = ((c - reply) == maxlen); if (again) { maxlen *= 2; FREE(reply); continue; } } *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; } multipath-tools-0.4.9/multipathd/cli_handlers.h000066400000000000000000000040171137574366600216700ustar00rootroot00000000000000int 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_status (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_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_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_quit(void * v, char ** reply, int * len, void * data); multipath-tools-0.4.9/multipathd/main.c000066400000000000000000001005501137574366600201570ustar00rootroot00000000000000/* * 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 /* * libcheckers */ #include /* * 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 "main.h" #include "pidfile.h" #include "uxlsnr.h" #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" #include "lock.h" #include "waiter.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 #define LOG_MSG(a,b) \ if (strlen(b)) condlog(a, "%s: %s - %s", pp->mpp->alias, pp->dev, b); pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; int logsink; /* * global copy of vecs for use in sig handlers */ struct vectors * gvecs; 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; int j; vector_foreach_slot (ompv, ompp, i) { 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 (!vector_alloc_slot(nmpv)) return 1; vector_set_slot(nmpv, ompp); setup_multipath(vecs, ompp); if ((j = find_slot(ompv, (void *)ompp)) != -1) vector_del_slot(ompv, j); continue; } else { dm_lib_release(); condlog(2, "%s devmap removed", 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) 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) { /* * 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 (dm_flush_map(mpp->alias)) { /* * May not really be an error -- if the map was already flushed * from the device mapper by dmsetup(8) for instance. */ condlog(0, "%s: can't flush", mpp->alias); return 1; } else { dm_lib_release(); condlog(2, "%s: devmap removed", mpp->alias); } orphan_paths(vecs->pathvec, mpp); remove_map_and_stop_waiter(mpp, vecs, 1); return 0; } static int uev_add_map (struct sysfs_device * dev, struct vectors * vecs) { condlog(2, "%s: add map (uevent)", dev->kernel); return ev_add_map(dev, vecs); } int ev_add_map (struct sysfs_device * dev, struct vectors * vecs) { char * alias; char *dev_t; int major, minor; char * refwwid; struct multipath * mpp; int map_present; int r = 1; dev_t = sysfs_attr_get_value(dev->devpath, "dev"); if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2) return 1; alias = dm_mapname(major, minor); if (!alias) return 1; map_present = dm_map_present(alias); if (map_present && dm_type(alias, TGT_MPATH) <= 0) { condlog(4, "%s: not a multipath map", alias); FREE(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 */ condlog(0, "%s: devmap already registered", dev->kernel); FREE(alias); return 0; } /* * now we can register the map */ if (map_present && (mpp = add_map_without_path(vecs, minor, alias))) { sync_map_state(mpp); condlog(2, "%s: devmap %s added", alias, dev->kernel); return 0; } refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec); if (refwwid) { r = coalesce_paths(vecs, NULL, refwwid, 0); dm_lib_release(); } if (!r) condlog(2, "%s: devmap %s added", alias, dev->kernel); else condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel); FREE(refwwid); FREE(alias); return r; } static int uev_remove_map (struct sysfs_device * dev, struct vectors * vecs) { condlog(2, "%s: remove map (uevent)", dev->kernel); return ev_remove_map(dev->kernel, vecs); } int ev_remove_map (char * devname, struct vectors * vecs) { struct multipath * mpp; mpp = find_mp_by_str(vecs->mpvec, devname); if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", devname); return 0; } flush_map(mpp, vecs); return 0; } static int uev_umount_map (struct sysfs_device * dev, struct vectors * vecs) { struct multipath * mpp; condlog(2, "%s: umount map (uevent)", dev->kernel); mpp = find_mp_by_str(vecs->mpvec, dev->kernel); if (!mpp) return 0; update_mpp_paths(mpp, vecs->pathvec); verify_paths(mpp, vecs, NULL); if (!VECTOR_SIZE(mpp->paths)) flush_map(mpp, vecs); return 0; } static int uev_add_path (struct sysfs_device * dev, struct vectors * vecs) { condlog(2, "%s: add path (uevent)", dev->kernel); return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1; } /* * returns: * 0: added * 1: error * 2: blacklisted */ int ev_add_path (char * devname, struct vectors * vecs) { struct multipath * mpp; struct path * pp; char empty_buff[WWID_SIZE] = {0}; if (strstr(devname, "..") != NULL) { /* * Don't allow relative device names in the pathvec */ condlog(0, "%s: path name is invalid", devname); return 1; } pp = find_path_by_dev(vecs->pathvec, devname); if (pp) { condlog(0, "%s: spurious uevent, path already in pathvec", devname); if (pp->mpp) return 0; } else { /* * get path vital state */ if (!(pp = store_pathinfo(vecs->pathvec, conf->hwtable, devname, DI_ALL))) { condlog(0, "%s: failed to store path info", devname); return 1; } pp->checkint = conf->checkint; } /* * need path UID to go any further */ if (memcmp(empty_buff, pp->wwid, WWID_SIZE) == 0) { condlog(0, "%s: failed to get path uid", devname); return 1; /* leave path added to pathvec */ } if (filter_path(conf, pp) > 0){ int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return 2; } mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); rescan: if (mpp) { if ((!pp->size) || (mpp->size != pp->size)) { if (!pp->size) condlog(0, "%s: failed to add new path %s, " "device size is 0", devname, pp->dev); else condlog(0, "%s: failed to add new path %s, " "device size mismatch", devname, 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)) return 1; /* leave path added to pathvec */ verify_paths(mpp, vecs, NULL); mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; } else { if (!pp->size) { condlog(0, "%s: failed to create new map," " %s device size is 0 ", devname, 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: creating new map", pp->dev); if ((mpp = add_map_with_path(vecs, pp, 1))) mpp->action = ACT_CREATE; else return 1; /* leave path added to pathvec */ } /* * push the map to the device-mapper */ if (setup_map(mpp)) { condlog(0, "%s: failed to setup map for addition of new " "path %s", mpp->alias, devname); goto out; } /* * reload the map for the multipath mapped device */ if (domap(mpp) <= 0) { condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, devname); /* * deal with asynchronous uevents :(( */ if (mpp->action == ACT_RELOAD) { condlog(0, "%s: uev_add_path sleep", mpp->alias); sleep(1); update_mpp_paths(mpp, vecs->pathvec); goto rescan; } else goto out; } dm_lib_release(); /* * update our state from kernel regardless of create or reload */ if (setup_multipath(vecs, mpp)) goto out; sync_map_state(mpp); if (mpp->action == ACT_CREATE && start_waiter_thread(mpp, vecs)) goto out; condlog(2, "%s path added to devmap %s", devname, mpp->alias); return 0; out: remove_map(mpp, vecs, 1); return 1; } static int uev_remove_path (struct sysfs_device * dev, struct vectors * vecs) { int retval; condlog(2, "%s: remove path (uevent)", dev->kernel); retval = ev_remove_path(dev->kernel, vecs); if (!retval) sysfs_device_put(dev); return retval; } int ev_remove_path (char * devname, struct vectors * vecs) { struct multipath * mpp; struct path * pp; int i, retval = 0; pp = find_path_by_dev(vecs->pathvec, devname); if (!pp) { /* Not an error; path might have been purged earlier */ condlog(0, "%s: path already removed", devname); return 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)) { condlog(2, "%s: removed map after" " removing all paths", alias); retval = 0; goto out; } /* * Not an error, continue */ } if (setup_map(mpp)) { condlog(0, "%s: failed to setup map for" " removal of path %s", mpp->alias, devname); goto fail; } /* * reload the map */ mpp->action = ACT_RELOAD; if (domap(mpp) <= 0) { condlog(0, "%s: failed in domap for " "removal of path %s", mpp->alias, devname); retval = 1; } else { /* * update our state from kernel */ if (setup_multipath(vecs, mpp)) { goto fail; } sync_map_state(mpp); condlog(2, "%s: path removed from map %s", devname, 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 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); 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 sysfs_device *sysdev; struct vectors * vecs; vecs = (struct vectors *)trigger_data; if (uev_discard(uev->devpath)) return 0; sysdev = sysfs_device_get(uev->devpath); if(!sysdev) return 0; lock(vecs->lock); /* * device map event * Add events are ignored here as the tables * are not fully initialised then. */ if (!strncmp(sysdev->kernel, "dm-", 3)) { if (!strncmp(uev->action, "change", 6)) { r = uev_add_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "umount", 6)) { r = uev_umount_map(sysdev, vecs); goto out; } goto out; } /* * path add/remove event */ if (filter_devnode(conf->blist_devnode, conf->elist_devnode, sysdev->kernel) > 0) goto out; if (!strncmp(uev->action, "add", 3)) { r = uev_add_path(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_path(sysdev, vecs); goto out; } out: unlock(vecs->lock); return r; } static void * ueventloop (void * ap) { block_signal(SIGUSR1, NULL); block_signal(SIGHUP, NULL); if (uevent_listen(&uev_trigger, ap)) fprintf(stderr, "error starting uevent listener"); return NULL; } static void * uxlsnrloop (void * ap) { block_signal(SIGUSR1, NULL); block_signal(SIGHUP, NULL); 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+MAPS, cli_list_maps); set_handler_callback(LIST+STATUS, cli_list_status); 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+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(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); umask(077); uxsock_listen(&uxsock_trigger, ap); return NULL; } static int exit_daemon (int status) { if (status != 0) fprintf(stderr, "bad exit status. see daemon.log\n"); condlog(3, "unlink pidfile"); unlink(DEFAULT_PIDFILE); pthread_mutex_lock(&exit_mutex); pthread_cond_signal(&exit_cond); pthread_mutex_unlock(&exit_mutex); return status; } 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 void reinstate_path (struct path * pp, int add_active) { if (!pp->mpp) return; if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) condlog(0, "%s: reinstate failed", pp->dev_t); else { condlog(2, "%s: reinstated", pp->dev_t); if (add_active) update_queue_mode_add_path(pp->mpp); } } 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--; } } } 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); } } } } void check_path (struct vectors * vecs, struct path * pp) { int newstate; if (!pp->mpp) return; if (pp->tick && --pp->tick) return; /* don't check this path yet */ /* * provision a next check soonest, * in case we exit abnormaly from here */ pp->tick = conf->checkint; newstate = get_state(pp, 1); if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { condlog(2, "%s: unusable path", pp->dev); pathinfo(pp, conf->hwtable, 0); return; } /* * Async IO in flight. Keep the previous path state * and reschedule as soon as possible */ if (newstate == PATH_PENDING) { pp->tick = 1; return; } if (newstate != pp->state) { int oldstate = pp->state; pp->state = newstate; 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 || update_multipath_strings(pp->mpp, vecs->pathvec)) { /* * proactively fail path in the DM */ if (oldstate == PATH_UP || oldstate == PATH_GHOST) fail_path(pp, 1); else fail_path(pp, 0); /* * cancel scheduled failback */ pp->mpp->failback_tick = 0; pp->mpp->stat_path_failures++; return; } /* * reinstate this path */ if (oldstate != PATH_UP && oldstate != PATH_GHOST) reinstate_path(pp, 1); else reinstate_path(pp, 0); /* * schedule [defered] failback */ if (pp->mpp->pgfailback > 0) pp->mpp->failback_tick = pp->mpp->pgfailback + 1; else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE && need_switch_pathgroup(pp->mpp, 1)) switch_pathgroup(pp->mpp); /* * 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) { LOG_MSG(4, checker_message(&pp->checker)); /* * 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; pp->tick = pp->checkint; condlog(4, "%s: delay next check %is", pp->dev_t, pp->tick); } else if (newstate == PATH_DOWN) LOG_MSG(2, checker_message(&pp->checker)); pp->state = newstate; /* * path prio refreshing */ condlog(4, "path prio refresh"); pathinfo(pp, conf->hwtable, DI_PRIO); /* * pathgroup failback policy */ if (need_switch_pathgroup(pp->mpp, 0)) { if (pp->mpp->pgfailback > 0 && pp->mpp->failback_tick <= 0) pp->mpp->failback_tick = pp->mpp->pgfailback + 1; else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) switch_pathgroup(pp->mpp); } } static void * checkerloop (void *ap) { struct vectors *vecs; struct path *pp; int count = 0; unsigned int i; sigset_t old; 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) { block_signal(SIGHUP, &old); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(vecs->lock); condlog(4, "tick"); if (vecs->pathvec) { vector_foreach_slot (vecs->pathvec, pp, i) { 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); pthread_sigmask(SIG_SETMASK, &old, NULL); sleep(1); } return NULL; } int configure (struct vectors * vecs, int start_waiters) { struct multipath * mpp; struct path * pp; vector mpvec; int i; 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 */ path_discovery(vecs->pathvec, conf, DI_ALL); 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, 0)) 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); /* * 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; /* * 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; if (load_config(DEFAULT_CONFIGFILE)) return 1; conf->verbosity = old->verbosity; if (!conf->checkint) { conf->checkint = DEFAULT_CHECKINT; conf->max_checkint = MAX_CHECKINT(conf->checkint); } conf->daemon = 1; configure(vecs, 1); free_config(old); return 0; } 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); } static void sighup (int sig) { condlog(2, "reconfigure (SIGHUP)"); lock(gvecs->lock); reconfigure(gvecs); unlock(gvecs->lock); #ifdef _DEBUG_ dbg_free_final(NULL); #endif } static void sigend (int sig) { exit_daemon(0); } static void sigusr1 (int sig) { condlog(3, "SIGUSR1 received"); } static void signal_init(void) { signal_set(SIGHUP, sighup); signal_set(SIGUSR1, sigusr1); 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 (int val) { FILE *fp; fp = fopen("/proc/self/oom_adj", "w"); if (!fp) return; fprintf(fp, "%i", val); fclose(fp); } 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); } } static int child (void * param) { pthread_t check_thr, uevent_thr, uxlsnr_thr; pthread_attr_t log_attr, misc_attr; struct vectors * vecs; struct multipath * mpp; int i; mlockall(MCL_CURRENT | MCL_FUTURE); setup_thread_attr(&misc_attr, 64 * 1024, 1); setup_thread_attr(&waiter_attr, 32 * 1024, 1); if (logsink) { setup_thread_attr(&log_attr, 64 * 1024, 0); log_thread_start(&log_attr); pthread_attr_destroy(&log_attr); } condlog(2, "--------start up--------"); condlog(2, "read " DEFAULT_CONFIGFILE); if (load_config(DEFAULT_CONFIGFILE)) exit(1); if (init_checkers()) { condlog(0, "failed to initialize checkers"); exit(1); } if (init_prio()) { condlog(0, "failed to initialize prioritizers"); exit(1); } setlogmask(LOG_UPTO(conf->verbosity + 3)); /* * fill the voids left in the config file */ if (!conf->checkint) { conf->checkint = DEFAULT_CHECKINT; conf->max_checkint = MAX_CHECKINT(conf->checkint); } 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\n", conf->max_fds, strerror(errno)); } if (pidfile_create(DEFAULT_PIDFILE, getpid())) { if (logsink) log_thread_stop(); exit(1); } signal_init(); setscheduler(); set_oom_adj(-16); vecs = gvecs = init_vecs(); if (!vecs) exit(1); if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { condlog(0, "can not find sysfs mount point"); exit(1); } conf->daemon = 1; dm_udev_set_sync_support(0); /* * fetch and configure both paths and multipaths */ if (configure(vecs, 1)) { condlog(0, "failure during configuration"); exit(1); } /* * start threads */ pthread_create(&check_thr, &misc_attr, checkerloop, vecs); pthread_create(&uevent_thr, &misc_attr, ueventloop, vecs); pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs); pthread_attr_destroy(&misc_attr); pthread_mutex_lock(&exit_mutex); pthread_cond_wait(&exit_cond, &exit_mutex); /* * exit path */ block_signal(SIGHUP, NULL); 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); free_pathvec(vecs->pathvec, FREE_PATHS); pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); sysfs_cleanup(); free_keys(keys); keys = NULL; free_handlers(handlers); handlers = NULL; free_polls(); 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..\n", 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; condlog(2, "--------shut down-------"); if (logsink) log_thread_stop(); dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); /* * 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; #ifdef _DEBUG_ dbg_free_final(NULL); #endif exit(0); } static int daemonize(void) { int pid; int in_fd, out_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); in_fd = open("/dev/null", O_RDONLY); if (in_fd < 0){ fprintf(stderr, "cannot open /dev/null for input : %s\n", strerror(errno)); _exit(0); } out_fd = open("/dev/console", O_WRONLY); if (out_fd < 0){ fprintf(stderr, "cannot open /dev/console for output : %s\n", strerror(errno)); _exit(0); } close(STDIN_FILENO); dup(in_fd); close(STDOUT_FILENO); dup(out_fd); close(STDERR_FILENO); dup(out_fd); close(in_fd); close(out_fd); if (chdir("/") < 0) fprintf(stderr, "cannot chdir to '/', continuing\n"); return 0; } int main (int argc, char *argv[]) { extern char *optarg; extern int optind; int arg; int err; logsink = 1; dm_init(); if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } /* make sure we don't lock any path */ chdir("/"); umask(umask(077) | 022); conf = alloc_config(); if (!conf) exit(1); while ((arg = getopt(argc, argv, ":dv:k::")) != EOF ) { switch(arg) { case 'd': 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 'k': uxclnt(optarg); exit(0); default: ; } } if (optind < argc) { char cmd[CMDSIZE]; char * s = cmd; char * c = s; 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); exit(0); } if (!logsink) err = 0; else err = daemonize(); if (err < 0) /* error */ exit(1); else if (err > 0) /* parent dies */ exit(0); else /* child lives */ return (child(NULL)); } multipath-tools-0.4.9/multipathd/main.h000066400000000000000000000005311137574366600201620ustar00rootroot00000000000000#ifndef MAIN_H #define MAIN_H #define MAPGCINT 5 int reconfigure (struct vectors *); int ev_add_path (char *, struct vectors *); int ev_remove_path (char *, struct vectors *); int ev_add_map (struct sysfs_device *, struct vectors *); int ev_remove_map (char *, struct vectors *); void sync_map_state (struct multipath *); #endif /* MAIN_H */ multipath-tools-0.4.9/multipathd/multipathd.8000066400000000000000000000100021137574366600213230ustar00rootroot00000000000000.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 Forground Mode. Don't daemonize, and print all messages to stdout and stderr. .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 -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 maps|multipaths Show the multipath devices that the multipathd is monitoring. .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 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 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 $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 $map Stop monitoring a multipath device. .TP .B resize map|multipath $map Resizes map $map to the given size .TP .B switch|switchgroup map $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 fail path $path Sets path $path into failed state. .TP .B reinstate path $path Resumes path $path from failed state. .TP .B disablequeueing map|multipath $map Disabled queuing on multipathed map $map .TP .B restorequeueing map|multipath $map Restore queuing on multipahted map $map .SH "SEE ALSO" .BR multipath (8) .BR kpartx (8) .BR hotplug (8) .SH "AUTHORS" .B multipathd was developed by Christophe Varoqui, and others. multipath-tools-0.4.9/multipathd/multipathd.init.debian000066400000000000000000000010641137574366600233500ustar00rootroot00000000000000#!/bin/sh PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/usr/bin/multipathd PIDFILE=/var/run/multipathd.pid 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 "." if [ -f $PIDFILE ] then kill `cat $PIDFILE` else echo "multipathd not running: Nothing to stop..." fi ;; 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.4.9/multipathd/multipathd.init.redhat000066400000000000000000000047271137574366600234060ustar00rootroot00000000000000#!/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 } stop() { 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_stop } restart() { stop start } force_restart() { force_stop start } reload() { echo -n "Reloading $prog: " trap "" SIGHUP killproc $DAEMON -HUP RETVAL=$? echo } case "$1" in start) start ;; stop) 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.4.9/multipathd/multipathd.init.suse000066400000000000000000000052431137574366600231100ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. # # Author: Hannes Reinecke # # init.d/routed # # and symbolic its link # # /usr/sbin/rcrouted # ### BEGIN INIT INFO # Provides: multipathd # Required-Start: $syslog # Required-Stop: # Default-Start: 3 5 # Default-Stop: 0 1 2 4 6 # Description: Starts multipath daemon ### END INIT INFO PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/sbin/multipathd PIDFILE=/var/run/multipathd.pid # Set the maximum number of open files MAX_OPEN_FDS=4096 test -x $DAEMON || exit 5 . /etc/rc.status # First reset status of this service rc_reset case "$1" in start) echo -n "Starting multipathd" modprobe dm-multipath # Set the maximum number of open files if [ -n "$MAX_OPEN_FDS" ] ; then ulimit -n $MAX_OPEN_FDS fi if [ -f $PIDFILE ]; then PID="$(cat $PIDFILE)" PROCNAME="$(ps -o cmd --no-headers $PID)" fi if [ "$PROCNAME" != "$DAEMON" ]; then $DAEMON fi # Remember status and be verbose rc_status -v sleep 1 ;; stop) echo -n "Shutting down multipathd" # Because of the way how multipathd sets up its own namespace # and chroots to it, killproc cannot be used with this process. # So implement a cruder version: if [ -f $PIDFILE ]; then PID="$(cat $PIDFILE)" PROCNAME="$(ps -o cmd --no-headers $PID)" fi if [ "$PROCNAME" == "$DAEMON" ]; then kill -TERM $PID 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 (!) # If it does not support reload: exit 3 ;; 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 if [ -f $PIDFILE ]; then PID="$(cat $PIDFILE)" PROCNAME="$(ps -o cmd --no-headers $PID)" if [ "$PROCNAME" == "$DAEMON" ]; then (exit 0) else (exit 1) fi 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.4.9/multipathd/pidfile.c000066400000000000000000000034241137574366600206510ustar00rootroot00000000000000#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.4.9/multipathd/pidfile.h000066400000000000000000000000641137574366600206530ustar00rootroot00000000000000int pidfile_create(const char *pidFile, pid_t pid); multipath-tools-0.4.9/multipathd/uxclnt.c000066400000000000000000000035161137574366600205540ustar00rootroot00000000000000/* * 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 "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++); } } /* * process the client */ static void process(int fd) { char *line; char *reply; 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 (!strncmp(line, "exit", 4) && llen == 4) break; if (!strncmp(line, "quit", 4) && llen == 4) break; if (send_packet(fd, line, llen + 1) != 0) break; if (recv_packet(fd, &reply, &len) != 0) break; print_reply(reply); if (line && *line) add_history(line); free(line); FREE(reply); } } static void process_req(int fd, char * inbuf) { char *reply; size_t len; if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) return; if (recv_packet(fd, &reply, &len) == 0) { print_reply(reply); FREE(reply); } } /* * entry point */ int uxclnt(char * inbuf) { int fd; fd = ux_socket_connect(DEFAULT_SOCKET); if (fd == -1) { perror("ux_socket_connect"); exit(1); } if (inbuf) process_req(fd, inbuf); else process(fd); return 0; } multipath-tools-0.4.9/multipathd/uxclnt.h000066400000000000000000000000321137574366600205470ustar00rootroot00000000000000int uxclnt(char * inbuf); multipath-tools-0.4.9/multipathd/uxlsnr.c000066400000000000000000000060271137574366600205720ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ /* * A simple domain socket listener */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uxlsnr.h" #define SLEEP_TIME 5000 struct client { int fd; struct client *next, *prev; }; static struct client *clients; static unsigned num_clients; /* * 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; /* put it in our linked list */ c = (struct client *)MALLOC(sizeof(*c)); memset(c, 0, sizeof(*c)); c->fd = fd; c->next = clients; if (c->next) c->next->prev = c; clients = c; num_clients++; } /* * kill off a dead client */ static void dead_client(struct client *c) { close(c->fd); if (c->prev) c->prev->next = c->next; if (c->next) c->next->prev = c->prev; if (c == clients) clients = c->next; FREE(c); num_clients--; } void free_polls (void) { FREE(polls); } /* * entry point */ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), void * trigger_data) { int ux_sock; size_t len; int rlen; char *inbuf; char *reply; ux_sock = ux_socket_listen(DEFAULT_SOCKET); if (ux_sock == -1) { condlog(0, "ux_socket_listen error"); exit(1); } polls = (struct pollfd *)MALLOC(0); while (1) { struct client *c; int i, poll_count; /* setup for a poll */ polls = REALLOC(polls, (1+num_clients) * sizeof(*polls)); polls[0].fd = ux_sock; polls[0].events = POLLIN; /* setup the clients */ for (i=1, c = clients; c; i++, c = c->next) { polls[i].fd = c->fd; polls[i].events = POLLIN; } /* most of our life is spent in this call */ poll_count = poll(polls, i, SLEEP_TIME); if (poll_count == -1) { if (errno == EINTR) continue; /* something went badly wrong! */ condlog(0, "poll"); exit(1); } if (poll_count == 0) continue; /* see if a client wants to speak to us */ for (i=1, c = clients; c; i++) { struct client *next = c->next; if (polls[i].revents & POLLIN) { if (recv_packet(c->fd, &inbuf, &len) != 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); } FREE(reply); reply = NULL; } FREE(inbuf); } } c = next; } /* see if we got a new client */ if (polls[0].revents & POLLIN) { new_client(ux_sock); } } close(ux_sock); return NULL; } multipath-tools-0.4.9/multipathd/uxlsnr.h000066400000000000000000000002301137574366600205650ustar00rootroot00000000000000struct pollfd *polls; void free_polls(void); void * uxsock_listen(int (*uxsock_trigger) (char *, char **, int *, void *), void * trigger_data);