pax_global_header00006660000000000000000000000064135674302670014527gustar00rootroot0000000000000052 comment=370685ad523e8a724b8f4f312fe02c5085eda54f fence-virt-1.0.0/000077500000000000000000000000001356743026700135675ustar00rootroot00000000000000fence-virt-1.0.0/.gitignore000066400000000000000000000007761356743026700155710ustar00rootroot00000000000000/*/ autom4te.cache config.log configure include/config.h* Makefile y.tab.* *.o *.lo *.la .deps .libs .*version Makefile.in *.swp aclocal.m4 client/fence_virt client/fence_xvm common/libfence_virt.a compile config.guess config.status config.sub config/config.c config/libsimpleconfig.a depcomp fence_virtd.init fence_virtd.service fence-virt.spec include/stamp-h1 install-sh libtool ltmain.sh m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 missing server/fence_virtd fence-virt-* fence-virt-1.0.0/COPYING000066400000000000000000000431031356743026700146230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. fence-virt-1.0.0/Makefile.am000066400000000000000000000100321356743026700156170ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ ltmain.sh compile config.h.in config.h.in~ \ autoscan.log configure.scan test-driver \ m4/libtool.m4 m4/lt~obsolete.m4 m4/ltoptions.m4 \ m4/ltsugar.m4 m4/ltversion.m4 include/config.in* AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SPEC = $(PACKAGE_NAME).spec TARFILES = $(PACKAGE_NAME)-$(VERSION).tar.bz2 \ $(PACKAGE_NAME)-$(VERSION).tar.gz \ $(PACKAGE_NAME)-$(VERSION).tar.xz EXTRA_DIST = autogen.sh .version architecture.txt \ fence_virtd.init.in fence_virtd.service.in \ fence_virt.txt README README.md TODO \ include $(SPEC).in build-aux SUBDIRS = config common client server man all-local: fence_virtd.service clean-local: rm -f $(SPEC) fence_virtd.service distclean-local: rm -f $(PACKAGE_NAME)-*.tar.* $(PACKAGE_NAME)-*.sha256* tag-* fence_virtd.service: fence_virtd.service.in SBINDIR="@sbindir@"; \ INITCONFDIR="@initconfdir@"; \ cat $^ > $@ ; \ echo "EnvironmentFile=-$$INITCONFDIR/fence_virtd" >> $@ ;\ echo "ExecStart=$$SBINDIR/fence_virtd \$$FENCE_VIRTD_ARGS" >> $@ ## make rpm/srpm section. $(SPEC): $(SPEC).in .version config.status rm -f $@-t $@ date="`LC_ALL=C $(UTC_DATE_AT)$(SOURCE_EPOCH) "+%a %b %d %Y"`" && \ if [ -f $(abs_srcdir)/.tarball-version ]; then \ gitver="`cat $(abs_srcdir)/.tarball-version`" && \ rpmver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ elif [ "`git log -1 --pretty=format:x . 2>&1`" = "x" ]; then \ gitver="`GIT_DIR=$(abs_srcdir)/.git git describe --abbrev=4 --match='v*' HEAD 2>/dev/null`" && \ rpmver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \ alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \ vtag=`echo $$gitver | sed -e "s/-.*//g"` && \ numcomm=`GIT_DIR=$(abs_srcdir)/.git git rev-list $$vtag..HEAD | wc -l` && \ cd $(abs_srcdir) && \ git update-index --refresh > /dev/null 2>&1 || true && \ dirty=`git diff-index --name-only HEAD 2>/dev/null` && cd - 2>/dev/null; \ else \ gitver="`cd $(abs_srcdir); build-aux/git-version-gen .tarball-version .gitarchivever`" && \ rpmver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ fi && \ if [ -n "$$dirty" ]; then dirty="dirty"; else dirty=""; fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#%glo.*alpha.*##g" \ -e "s#%glo.*numcomm.*##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $(abs_srcdir)/$@.in > $@-t; \ else \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $(abs_srcdir)/$@.in > $@-t; \ fi; \ if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi sed -i -e "s#@defaultadmgroup@#$(DEFAULTADMGROUP)#g" $@-t chmod a-w $@-t mv $@-t $@ rm -f $@-t* $(TARFILES): $(MAKE) dist RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" srpm: clean $(MAKE) $(SPEC) $(TARFILES) rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean $(MAKE) $(SPEC) $(TARFILES) rpmbuild $(RPMBUILDOPTS) -ba $(SPEC) # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version echo $(SOURCE_EPOCH) > $(distdir)/source_epoch fence-virt-1.0.0/README000066400000000000000000000121321356743026700144460ustar00rootroot00000000000000TODO: update I. Fence_xvm - Virtual machine fencing agent Fence_xvm is an agent which establishes a communications link between a cluster of virtual machines (VC) and a cluster of domain0/physical nodes which are hosting the virtual cluster. Its operations are fairly simple. (a) Start a listener service. (b) Send a multicast packet requesting that a VM be fenced. (c) Authenticate client. (e) Read response. (f) Exit with success/failure, depending on the response received. If any of the above steps fail, the fencing agent exits with a failure code and fencing is retried by the virtual cluster at a later time. Because of the simplicty of fence_xvm, it is not necessary that fence_xvm be run from within a virtualized guest - all it needs is libnspr and libnss and a shared private key (for authentication; we would hate to receive a false positive response from a node not in the cluster!). II. Fence_virtd - Virtual machine fencing host Fence_virtd is a daemon which runs on physical hosts (e.g. in domain0) of the cluster hosting the virtual cluster. It listens on a port for multicast traffic from virtual cluster(s), and takes actions. Multiple disjoint virtual clusters can coexist on a single physical host cluster, but this requires multiple instances of fence_virtd. NOTE: fence_virtd *MUST* be run on ALL nodes in a given cluster which will be hosting virtual machines if fence_xvm is to be used for fencing! There are a couple of ways the multicast packet is handled, depending on the state of the host OS. It might be hosting the VM, or it might not. Furthermore, the VM might "reside" on a host which has failed. In order to be able to guarantee safe fencing of a VM even if the last- known host is down, we must store the last-known locations of each virtual machine in some sort of cluster-wide way. For this, we use the corosync CPG API. Every few seconds, fence_virtd queries the hypervisor via libvirt and stores any local VM states and sends those states over CPG to all other members. In the event of a physical node failure (which consequently causes the failure of one or more guests), we can then read the stored VM state corresponding to the guest we need to fence to find out the previous owner. With that information, we can infer if the known host node has been fenced. If so, then the VM is clean as well. The physical cluster must, therefore, have fencing in order for fence_virtd to work. Operation of a node hosting a VM which needs to be fenced: (a) Receive multicast packet (b) Authenticate multicast packet (c) Open connection to host contained within multicast packet. (d) Authenticate server. (e) Carry out fencing operation (e.g. call libvirt to destroy or reboot the VM; there is no "on" method at this point). (f) If operation succeeds, send success response. Operation of high-node-ID: (a) Receive multicast packet (b) Authenticate multicast packet (c) Read VM state from stored CPG messages (d) Check liveliness of nodeID hosting VM (if alive, do nothing) (e) Open connection to host contained within multicast packet. (f) Check with CMAN to see if last-known host has been fenced. (g) If last-known host has been fenced, send success response. (h) Authenticate server & send response. NOTE: There is always a possibility that a VM is started again before the fencing operation and CPG update for that VM occurs. If the VM has booted and rejoined the cluster, fencing will not be necessary. If it is in the process of booting, but has not yet joined the cluster, fencing will also not be necessary - because it will not be using cluster resources yet. III. Security considerations While fencing is generally expected to run on a more or less trusted network, there are cases where it may not be. * The multicast packet is subject to replay attacks, but because no fencing action is taken based solely on the information contained within the packet, this should not allow an attacker to maliciously fence a VM from outside the cluster, though it may be possible to cause a DoS of fence_virtd if enough multicast packets are sent. * The only currently supported authentication mechanisms are simple challenge-response based on a shared private key and pseudorandom number generation. * An attacker with access to the shared key(s) can easily fence any known VM, even if they are not on a cluster node. * Different shared keys should be used for different virtual clusters on the same subnet (whether in the same physical cluster or not). Additionally, multiple fence_virtd instances must be run (each listening on a different multicast IP + port combination). IV. Configuration Generate a random key file. An example of how to generate it is: dd if=/dev/urandom of=/etc/cluster/fence_xvm.key bs=4096 count=1 Distribute the generated key file to all domUs in a cluster as well as all dom0s which will be hosting that particular cluster of domUs. The key should not be placed on shared file systems (because shared file systems require the cluster, which requires fencing...). Start fence_virtd on all hosts Configure fence_xvm on the domU cluster... rest...tbd fence-virt-1.0.0/README.md000066400000000000000000000000621356743026700150440ustar00rootroot00000000000000fence-virt ========== Virtual Machine I/O Fencingfence-virt-1.0.0/TODO000066400000000000000000000002531356743026700142570ustar00rootroot00000000000000High Priority / Blockers for v1.0; * endian-clean / 64-bit clean data structure analysis Future Stuff: * clean up development bits so third parties can develop plugins fence-virt-1.0.0/architecture.txt000066400000000000000000000014201356743026700170070ustar00rootroot00000000000000The actual architecture of fence_virtd is very simple. We have a set of listener plugins which listens for fencing requests for virtual machines. These plugins are assigned callbacks which are entry functions in to the backend plugins. The backend plugins perform the actual fencing request. In the middle, we have only enough code to provide basic integration functions between the listener and backend plugins. This includes a very simple confiugration plugin which we pass to each of the plugins. Because we are passing function pointers in to the plugins themselves for configuration (rather than having the plugins call an API directly, for example), we are able to swap out the configuration subsystem for other, more full-featured configuration systems, such as libccs. fence-virt-1.0.0/autogen.sh000077500000000000000000000001671356743026700155740ustar00rootroot00000000000000#!/bin/sh # Run this to generate all the initial makefiles, etc. autoreconf -i -v && echo Now run ./configure and make fence-virt-1.0.0/build-aux/000077500000000000000000000000001356743026700154615ustar00rootroot00000000000000fence-virt-1.0.0/build-aux/git-version-gen000077500000000000000000000222751356743026700204340ustar00rootroot00000000000000#!/bin/sh # Print a version string. scriptversion=2018-08-31.20; # UTC # Copyright (C) 2012-2019 Red Hat, Inc. # Copyright (C) 2007-2016 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 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. # It may be run two ways: # - from a git repository in which the "git describe" command below # produces useful output (thus requiring at least one signed tag) # - from a non-git-repo directory containing a .tarball-version file, which # presumes this script is invoked like "./git-version-gen .tarball-version". # In order to use intra-version strings in your project, you will need two # separate generated version string files: # # .tarball-version - present only in a distribution tarball, and not in # a checked-out repository. Created with contents that were learned at # the last time autoconf was run, and used by git-version-gen. Must not # be present in either $(srcdir) or $(builddir) for git-version-gen to # give accurate answers during normal development with a checked out tree, # but must be present in a tarball when there is no version control system. # Therefore, it cannot be used in any dependencies. GNUmakefile has # hooks to force a reconfigure at distribution time to get the value # correct, without penalizing normal development with extra reconfigures. # # .version - present in a checked-out repository and in a distribution # tarball. Usable in dependencies, particularly for files that don't # want to depend on config.h but do want to track version changes. # Delete this file prior to any autoconf run where you want to rebuild # files to pick up a version string change; and leave it stale to # minimize rebuild time after unrelated changes to configure sources. # # As with any generated file in a VC'd directory, you should add # /.version to .gitignore, so that you don't accidentally commit it. # .tarball-version is never generated in a VC'd directory, so needn't # be listed there. # # In order to use git archive versions another two files has to be presented: # # .gitarchive-version - present in checked-out repository and git # archive tarball, but not in the distribution tarball. Used as a last # option for version. File must contain special string $Format:%d$, # which is substitued by git on archive operation. # # .gitattributes - present in checked-out repository and git archive # tarball, but not in the distribution tarball. Must set export-subst # attribute for .gitarchive-version file. # # Use the following line in your configure.ac, so that $(VERSION) will # automatically be up-to-date each time configure is run (and note that # since configure.ac no longer includes a version string, Makefile rules # should not depend on configure.ac for version updates). # # AC_INIT([GNU project], # m4_esyscmd([build-aux/git-version-gen .tarball-version]), # [bug-project@example]) # # Then use the following lines in your Makefile.am, so that .version # will be present for dependencies, and so that .version and # .tarball-version will exist in distribution tarballs. # # EXTRA_DIST = $(top_srcdir)/.version # BUILT_SOURCES = $(top_srcdir)/.version # $(top_srcdir)/.version: # echo $(VERSION) > $@-t && mv $@-t $@ # dist-hook: # echo $(VERSION) > $(distdir)/.tarball-version me=$0 version="git-version-gen $scriptversion Copyright 2011 Free Software Foundation, Inc. There is NO warranty. You may redistribute this software under the terms of the GNU General Public License. For more information about these matters, see the files named COPYING." usage="\ Usage: $me [OPTION]... \$srcdir/.tarball-version [\$srcdir/.gitarchive-version] [TAG-NORMALIZATION-SED-SCRIPT] Print a version string. Options: --prefix PREFIX prefix of git tags (default 'v') --fallback VERSION fallback version to use if \"git --version\" fails --help display this help and exit --version output version information and exit Running without arguments will suffice in most cases." prefix=v fallback= while test $# -gt 0; do case $1 in --help) echo "$usage"; exit 0;; --version) echo "$version"; exit 0;; --prefix) shift; prefix="$1";; --fallback) shift; fallback="$1";; -*) echo "$0: Unknown option '$1'." >&2 echo "$0: Try '--help' for more information." >&2 exit 1;; *) if test "x$tarball_version_file" = x; then tarball_version_file="$1" elif test "x$gitarchive_version_file" = x; then gitarchive_version_file="$1" elif test "x$tag_sed_script" = x; then tag_sed_script="$1" else echo "$0: extra non-option argument '$1'." >&2 exit 1 fi;; esac shift done if test "x$tarball_version_file" = x; then echo "$usage" exit 1 fi tag_sed_script="${tag_sed_script:-s/x/x/}" nl=' ' # Avoid meddling by environment variable of the same name. v= v_from_git= # First see if there is a tarball-only version file. # then try "git describe", then default. if test -f $tarball_version_file then v=`cat $tarball_version_file` || v= case $v in *$nl*) v= ;; # reject multi-line output [0-9]*) ;; *) v= ;; esac test "x$v" = x \ && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 fi if test "x$v" != x then : # use $v # Otherwise, if there is at least one git commit involving the working # directory, and "git describe" output looks sensible, use that to # derive a version string. elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \ || git describe --abbrev=4 HEAD 2>/dev/null` \ && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ && case $v in $prefix[0-9]*) ;; *) (exit 1) ;; esac then # Is this a new git that lists number of commits since the last # tag or the previous older version that did not? # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb case $v in *-*-*) : git describe is okay three part flavor ;; *-*) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version # of git describe. vtag=`echo "$v" | sed 's/-.*//'` commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \ || { commit_list=failed; echo "$0: WARNING: git rev-list failed" 1>&2; } numcommits=`echo "$commit_list" | wc -l` v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; test "$commit_list" = failed && v=UNKNOWN ;; esac # Change the first '-' to a '.', so version-comparing tools work properly. # Remove the "g" in git describe's output string, to save a byte. v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; v_from_git=1 elif test "x$fallback" = x || git --version >/dev/null 2>&1; then if test -f $gitarchive_version_file then v=`sed "s/^.*tag: \($prefix[0-9)][^,)]*\).*\$/\1/" $gitarchive_version_file \ | sed "$tag_sed_script"` || exit 1 case $v in *$nl*) v= ;; # reject multi-line output $prefix[0-9]*) ;; *) v= ;; esac test -z "$v" \ && echo "$0: WARNING: $gitarchive_version_file doesn't contain valid version tag" 1>&2 \ && v=UNKNOWN else v=UNKNOWN fi else v=$fallback fi if test "x$fallback" = x -a "$v" = "UNKNOWN" then echo "$0: ERROR: Can't find valid version. Please use valid git repository," \ "released tarball or version tagged archive" 1>&2 exit 1 fi v=`echo "$v" |sed "s/^$prefix//"` # Test whether to append the "-dirty" suffix only if the version # string we're using came from git. I.e., skip the test if it's "UNKNOWN" # or if it came from .tarball-version. if test "x$v_from_git" != x; then # Don't declare a version "dirty" merely because a time stamp has changed. git update-index --refresh > /dev/null 2>&1 dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= case "$dirty" in '') ;; *) # Append the suffix only if there isn't one already. case $v in *-dirty) ;; *) v="$v-dirty" ;; esac ;; esac fi # Omit the trailing newline, so that m4_esyscmd can use the result directly. printf %s "$v" # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: fence-virt-1.0.0/build-aux/gitlog-to-changelog000077500000000000000000000126451356743026700212510ustar00rootroot00000000000000eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' & eval 'exec perl -wS "$0" $argv:q' if 0; # Convert git log output to ChangeLog format. my $VERSION = '2009-10-30 13:46'; # UTC # The definition above must lie within the first 8 lines in order # for the Emacs time-stamp write hook (at end) to update it. # If you change this file with Emacs, please let the write hook # do its job. Otherwise, update this string manually. # Copyright (C) 2008-2010 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 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Written by Jim Meyering use strict; use warnings; use Getopt::Long; use POSIX qw(strftime); (my $ME = $0) =~ s|.*/||; # use File::Coda; # http://meyering.net/code/Coda/ END { defined fileno STDOUT or return; close STDOUT and return; warn "$ME: failed to close standard output: $!\n"; $? ||= 1; } sub usage ($) { my ($exit_code) = @_; my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); if ($exit_code != 0) { print $STREAM "Try `$ME --help' for more information.\n"; } else { print $STREAM < ChangeLog $ME -- -n 5 foo > last-5-commits-to-branch-foo EOF } exit $exit_code; } # If the string $S is a well-behaved file name, simply return it. # If it contains white space, quotes, etc., quote it, and return the new string. sub shell_quote($) { my ($s) = @_; if ($s =~ m![^\w+/.,-]!) { # Convert each single quote to '\'' $s =~ s/\'/\'\\\'\'/g; # Then single quote the string. $s = "'$s'"; } return $s; } sub quoted_cmd(@) { return join (' ', map {shell_quote $_} @_); } { my $since_date = '1970-01-01 UTC'; my $format_string = '%s%n%b%n'; GetOptions ( help => sub { usage 0 }, version => sub { print "$ME version $VERSION\n"; exit }, 'since=s' => \$since_date, 'format=s' => \$format_string, ) or usage 1; my @cmd = (qw (git log --log-size), "--since=$since_date", '--pretty=format:%ct %an <%ae>%n%n'.$format_string, @ARGV); open PIPE, '-|', @cmd or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); my $prev_date_line = ''; while (1) { defined (my $in = ) or last; $in =~ /^log size (\d+)$/ or die "$ME:$.: Invalid line (expected log size):\n$in"; my $log_nbytes = $1; my $log; my $n_read = read PIPE, $log, $log_nbytes; $n_read == $log_nbytes or die "$ME:$.: unexpected EOF\n"; my @line = split "\n", $log; my $author_line = shift @line; defined $author_line or die "$ME:$.: unexpected EOF\n"; $author_line =~ /^(\d+) (.*>)$/ or die "$ME:$.: Invalid line " . "(expected date/author/email):\n$author_line\n"; my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); # If this line would be the same as the previous date/name/email # line, then arrange not to print it. if ($date_line ne $prev_date_line) { $prev_date_line eq '' or print "\n"; print $date_line; } $prev_date_line = $date_line; # Omit "Signed-off-by..." lines. @line = grep !/^Signed-off-by: .*>$/, @line; # If there were any lines if (@line == 0) { warn "$ME: warning: empty commit message:\n $date_line\n"; } else { # Remove leading and trailing blank lines. while ($line[0] =~ /^\s*$/) { shift @line; } while ($line[$#line] =~ /^\s*$/) { pop @line; } # Prefix each non-empty line with a TAB. @line = map { length $_ ? "\t$_" : '' } @line; print "\n", join ("\n", @line), "\n"; } defined ($in = ) or last; $in ne "\n" and die "$ME:$.: unexpected line:\n$in"; } close PIPE or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; # FIXME-someday: include $PROCESS_STATUS in the diagnostic } # Local Variables: # mode: perl # indent-tabs-mode: nil # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "my $VERSION = '" # time-stamp-format: "%:y-%02m-%02d %02H:%02M" # time-stamp-time-zone: "UTC" # time-stamp-end: "'; # UTC" # End: fence-virt-1.0.0/build-aux/release.mk000066400000000000000000000043331356743026700174350ustar00rootroot00000000000000# # Copyright (C) 2012-2019 Red Hat, Inc. All rights reserved. # # Author: Fabio M. Di Nitto # # This software licensed under GPL-2.0+ # # to build official release tarballs, handle tagging and publish. # example: # make -f build-aux/release.mk all version=0.4.0 release=yes publish gpgsignkey = project = fence-virt deliverables = $(project)-$(version).sha256 \ $(project)-$(version).tar.bz2 \ $(project)-$(version).tar.gz \ $(project)-$(version).tar.xz .PHONY: all all: tag tarballs sign # first/last skipped per release/gpgsignkey respectively .PHONY: checks checks: ifeq (,$(version)) @echo ERROR: need to define version= @exit 1 endif @if [ ! -d .git ]; then \ echo This script needs to be executed from top level cluster git tree; \ exit 1; \ fi .PHONY: setup setup: checks ./autogen.sh ./configure make maintainer-clean .PHONY: tag tag: setup ./tag-$(version) tag-$(version): ifeq (,$(release)) @echo Building test release $(version), no tagging echo '$(version)' > .tarball-version else # following will be captured by git-version-gen automatically git tag -a -m "v$(version) release" v$(version) HEAD @touch $@ endif .PHONY: tarballs tarballs: tag ./autogen.sh ./configure #make distcheck (disabled.. needs root) make dist .PHONY: sha256 sha256: $(project)-$(version).sha256 # NOTE: dependency backtrack may fail trying to sign missing tarballs otherwise # (actually, only when signing tarballs directly, but doesn't hurt anyway) $(deliverables): tarballs $(project)-$(version).sha256: # checksum anything from deliverables except for in-prep checksums file sha256sum $(deliverables:$@=) | sort -k2 > $@ .PHONY: sign ifeq (,$(gpgsignkey)) sign: $(deliverables) @echo No GPG signing key defined else sign: $(deliverables:=.asc) endif # NOTE: cannot sign multiple files at once $(project)-$(version).%.asc: $(project)-$(version).% gpg --default-key "$(gpgsignkey)" \ --detach-sign \ --armor \ $< .PHONY: publish publish: ifeq (,$(release)) @echo Building test release $(version), no publishing! else @echo : pushing tags @git push --follow-tags origin endif .PHONY: clean clean: rm -rf $(project)-* tag-* .tarball-version fence-virt-1.0.0/client/000077500000000000000000000000001356743026700150455ustar00rootroot00000000000000fence-virt-1.0.0/client/Makefile.am000066400000000000000000000020611356743026700171000ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2 ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in sbin_PROGRAMS = fence_virt fence_virt_SOURCES = mcast.c serial.c main.c options.c tcp.c vsock.c fence_virt_CFLAGS = $(AM_CFLAGS) $(nss_CFLAGS) $(xml2_CFLAGS) $(PTHREAD_CFLAGS) fence_virt_LDFLAGS = $(AM_LDFLAGS) $(COMMON_LDFLAGS) fence_virt_LDADD = $(COMMON_LIBS) $(nss_LIBS) $(xml2_LIBS) $(PTHREAD_LIBS) if xvmcompat install-exec-hook: fence_virt (cd $(DESTDIR)/${sbindir}; $(LN_S) -nf $^ fence_xvm) uninstall-hook: (cd $(DESTDIR)/${sbindir}; rm -f fence_xvm) endif fence-virt-1.0.0/client/main.c000066400000000000000000000110521356743026700161340ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "options.h" #include "debug.h" #include "client.h" int main(int argc, char **argv) { fence_virt_args_t args; const char *my_options; int ret = 0; args_init(&args); if (!strcmp(basename(argv[0]), "fence_xvm")) { my_options = "di:a:p:r:C:c:k:M:H:uo:t:?hVw:"; args.mode = MODE_MULTICAST; } else { my_options = "dD:P:A:p:M:H:o:t:?hVT:S::C:c:k:w:"; args.mode = MODE_SERIAL; } if (argc == 1) { args_get_stdin(my_options, &args); } else { args_get_getopt(argc, argv, my_options, &args); } if (args.flags & F_HELP) { args_usage(argv[0], my_options, 0); printf("With no command line argument, arguments are " "read from standard input.\n"); printf("Arguments read from standard input take " "the form of:\n\n"); printf(" arg1=value1\n"); printf(" arg2=value2\n\n"); args_usage(argv[0], my_options, 1); exit(0); } if (args.flags & F_VERSION) { printf("%s %s\n", basename(argv[0]), XVM_VERSION); #ifdef VERSION printf("fence release %s\n", VERSION); #endif exit(0); } openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON); args_finalize(&args); dset(args.debug); if (args.debug > 0) args_print(&args); /* Additional validation here */ if (!args.domain && (args.op != FENCE_DEVSTATUS && args.op != FENCE_HOSTLIST && args.op != FENCE_METADATA)) { printf("No domain specified!\n"); syslog(LOG_NOTICE, "No domain specified"); args.flags |= F_ERR; } if (args.net.ipaddr) args.mode = MODE_TCP; if (args.net.cid >= 2) args.mode = MODE_VSOCK; if (args.flags & F_ERR) { if (args.op != FENCE_VALIDATEALL) args_usage(argv[0], my_options, (argc == 1)); exit(1); } if (args.op == FENCE_VALIDATEALL) exit(0); if (args.op == FENCE_METADATA) { args_metadata(argv[0], my_options); exit(0); } if (args.delay > 0 && args.op != FENCE_STATUS && args.op != FENCE_DEVSTATUS && args.op != FENCE_HOSTLIST) sleep(args.delay); switch(args.mode) { case MODE_MULTICAST: ret = mcast_fence_virt(&args); break; case MODE_SERIAL: ret = serial_fence_virt(&args); break; case MODE_TCP: ret = tcp_fence_virt(&args); break; case MODE_VSOCK: ret = vsock_fence_virt(&args); break; default: ret = 1; goto out; } switch(ret) { case RESP_OFF: if (args.op == FENCE_STATUS) printf("Status: OFF\n"); else if (args.domain) syslog(LOG_NOTICE, "Domain \"%s\" is OFF", args.domain); break; case 0: if (args.op == FENCE_STATUS) printf("Status: ON\n"); else if (args.domain) syslog(LOG_NOTICE, "Domain \"%s\" is ON", args.domain); break; case RESP_FAIL: if (args.domain) { syslog(LOG_NOTICE, "Fence operation failed for domain \"%s\"", args.domain); } else syslog(LOG_NOTICE, "Fence operation failed"); printf("Operation failed\n"); break; case RESP_PERM: if (args.domain) { syslog(LOG_NOTICE, "Permission denied for Fence operation for domain \"%s\"", args.domain); } else syslog(LOG_NOTICE, "Permission denied for fence operation"); printf("Permission denied\n"); break; default: if (args.domain) { syslog(LOG_NOTICE, "Unknown response (%d) for domain \"%s\"", ret, args.domain); } else syslog(LOG_NOTICE, "Unknown response (%d)", ret); printf("Unknown response (%d)\n", ret); break; } out: closelog(); exit(ret); } fence-virt-1.0.0/client/mcast.c000066400000000000000000000203451356743026700163240ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "ip_lookup.h" #include "simple_auth.h" #include "options.h" #include "tcp.h" #include "mcast.h" #include "debug.h" #include "fdops.h" static int tcp_wait_connect(int lfd, int retry_tenths) { int fd; fd_set rfds; int n; struct timeval tv; dbg_printf(3, "Waiting for connection from XVM host daemon.\n"); FD_ZERO(&rfds); FD_SET(lfd, &rfds); tv.tv_sec = retry_tenths / 10; tv.tv_usec = (retry_tenths % 10) * 100000; n = select(lfd + 1, &rfds, NULL, NULL, &tv); if (n == 0) { errno = ETIMEDOUT; return -1; } else if (n < 0) { return -1; } fd = accept(lfd, NULL, 0); if (fd < 0) return -1; return fd; } void do_read_hostlist(int fd, int timeout) { host_state_t hinfo; fd_set rfds; struct timeval tv; int ret; do { FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = _select_retry(fd+1, &rfds, NULL, NULL, &tv); if (ret == 0) { printf("Timed out!\n"); break; } ret = _read_retry(fd, &hinfo, sizeof(hinfo), &tv); if (ret < sizeof(hinfo)) { printf("Bad read!\n"); break; } if (strlen((char *)hinfo.uuid) == 0 && strlen((char *)hinfo.domain) == 0) break; printf("%-32s %s %s\n", hinfo.domain, hinfo.uuid, (hinfo.state == 1) ? "on" : "off"); } while (1); } static int tcp_exchange(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { fd_set rfds; struct timeval tv; char ret = 1; /* Ok, we're connected */ dbg_printf(3, "Issuing TCP challenge\n"); if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) { /* Challenge failed */ printf("Invalid response to challenge\n"); return 1; } /* Now they'll send us one, so we need to respond here */ dbg_printf(3, "Responding to TCP challenge\n"); if (sock_response(fd, auth, key, key_len, timeout) <= 0) { printf("Invalid response to challenge\n"); return 1; } dbg_printf(2, "TCP Exchange + Authentication done... \n"); FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = 1; dbg_printf(3, "Waiting for return value from XVM host\n"); if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) return -1; /* Read return code */ if (_read_retry(fd, &ret, 1, &tv) < 0) ret = 1; if (ret == (char)RESP_HOSTLIST) /* hostlist */ { do_read_hostlist(fd, timeout); ret = 0; } return ret; } static int send_multicast_packets(ip_list_t *ipl, fence_virt_args_t *args, uint32_t seqno, void *key, size_t key_len) { fence_req_t freq; int mc_sock; ip_addr_t *ipa; struct sockaddr_in tgt4; struct sockaddr_in6 tgt6; struct sockaddr *tgt; socklen_t tgt_len; for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) { if (ipa->ipa_family != args->net.family) { dbg_printf(2, "Ignoring %s: wrong family\n", ipa->ipa_address); continue; } if (args->net.family == PF_INET) { mc_sock = ipv4_send_sk(ipa->ipa_address, args->net.addr, args->net.port, (struct sockaddr *)&tgt4, sizeof(struct sockaddr_in)); tgt = (struct sockaddr *)&tgt4; tgt_len = sizeof(tgt4); } else if (args->net.family == PF_INET6) { mc_sock = ipv6_send_sk(ipa->ipa_address, args->net.addr, args->net.port, (struct sockaddr *)&tgt6, sizeof(struct sockaddr_in6)); tgt = (struct sockaddr *)&tgt6; tgt_len = sizeof(tgt6); } else { dbg_printf(2, "Unsupported family %d\n", args->net.family); return -1; } if (mc_sock < 0) continue; /* Build our packet */ memset(&freq, 0, sizeof(freq)); if (args->domain && strlen((char *)args->domain)) { strncpy((char *)freq.domain, args->domain, sizeof(freq.domain) - 1); } freq.request = args->op; freq.hashtype = args->net.hash; freq.seqno = seqno; /* Store source address */ if (ipa->ipa_family == PF_INET) { freq.addrlen = sizeof(struct in_addr); /* XXX Swap order for in_addr ? XXX */ inet_pton(PF_INET, ipa->ipa_address, freq.address); } else if (ipa->ipa_family == PF_INET6) { freq.addrlen = sizeof(struct in6_addr); inet_pton(PF_INET6, ipa->ipa_address, freq.address); } freq.flags = 0; if (args->flags & F_USE_UUID) freq.flags |= RF_UUID; freq.family = ipa->ipa_family; freq.port = args->net.port; sign_request(&freq, key, key_len); dbg_printf(3, "Sending to %s via %s\n", args->net.addr, ipa->ipa_address); sendto(mc_sock, &freq, sizeof(freq), 0, (struct sockaddr *)tgt, tgt_len); close(mc_sock); } return 0; } /* TODO: Clean this up!!! */ int mcast_fence_virt(fence_virt_args_t *args) { ip_list_t ipl; char key[MAX_KEY_LEN]; struct timeval tv; int lfd = -1, key_len = 0, fd, ret; int attempts = 0; uint32_t seqno; /* Initialize NSS; required to do hashing, as silly as that sounds... */ if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) { key_len = read_key_file(args->net.key_file, key, sizeof(key)); if (key_len < 0) { printf("Could not read %s; trying without " "authentication\n", args->net.key_file); args->net.auth = AUTH_NONE; args->net.hash = HASH_NONE; key_len = 0; } } /* Do the real work */ if (ip_build_list(&ipl) < 0) { printf("Error building IP address list\n"); return 1; } attempts = args->timeout * 10 / args->retr_time; listen_loop: do { switch (args->net.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: if (args->net.family == PF_INET) { lfd = ipv4_listen(NULL, args->net.port, 10); } else { lfd = ipv6_listen(NULL, args->net.port, 10); } break; /*case AUTH_X509:*/ /* XXX Setup SSL listener socket here */ default: return 1; } if (lfd < 0) { printf("Failed to listen: %s\n", strerror(errno)); usleep(args->retr_time * 100000); if (--attempts > 0) goto listen_loop; } } while (0); if (lfd < 0) return -1; gettimeofday(&tv, NULL); seqno = (uint32_t)tv.tv_usec; do { if (send_multicast_packets(&ipl, args, seqno, key, key_len)) { close(lfd); return -1; } switch (args->net.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: fd = tcp_wait_connect(lfd, args->retr_time); if (fd < 0 && (errno == ETIMEDOUT || errno == EINTR)) continue; break; /* case AUTH_X509: ... = ssl_wait_connect... */ break; default: close(lfd); return 1; } break; } while (--attempts); if (lfd >= 0) close(lfd); if (fd < 0) { if (attempts <= 0) { printf("Timed out waiting for response\n"); return 1; } printf("Operation failed: %s\n", strerror(errno)); return -1; } switch (args->net.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: ret = tcp_exchange(fd, args->net.auth, key, key_len, args->timeout); close(fd); return ret; break; /* case AUTH_X509: return ssl_exchange(...); */ default: close(fd); return 1; } close(fd); return 1; } fence-virt-1.0.0/client/options.c000066400000000000000000000514711356743026700167140ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "mcast.h" #include "tcp_listener.h" #include "options.h" #define SCHEMA_COMPAT '\xfe' /* Assignment functions */ static inline void assign_debug(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) { /* GNU getopt sets optarg to NULL for options w/o a param We rely on this here... */ args->debug++; return; } args->debug = atoi(value); if (args->debug < 0) { args->debug = 1; } } static inline void assign_family(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (!strcasecmp(value, "ipv4")) { args->net.family = PF_INET; } else if (!strcasecmp(value, "ipv6")) { args->net.family = PF_INET6; } else if (!strcasecmp(value, "auto")) { args->net.family = 0; } else { printf("Unsupported family: '%s'\n", value); args->flags |= F_ERR; } } static inline void assign_address(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (args->net.addr) free(args->net.addr); args->net.addr = strdup(value); } static inline void assign_ip_address(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (args->net.ipaddr) free(args->net.ipaddr); args->net.ipaddr = strdup(value); } static inline void assign_channel_address(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (args->serial.address) free(args->serial.address); args->serial.address = strdup(value); } static inline void assign_port(fence_virt_args_t *args, struct arg_info *arg, char *value) { char *p; int ret; if (!value) return; ret = strtol(value, &p, 0); if (ret <= 0 || ret >= 65536 || *p != '\0') { printf("Invalid port: '%s'\n", value); args->flags |= F_ERR; } else args->net.port = ret; } static inline void assign_cid(fence_virt_args_t *args, struct arg_info *arg, char *value) { char *p; unsigned long ret; if (!value) { args->net.cid = 2; return; } ret = strtoul(value, &p, 0); if (!p || *p != '\0' || ret < 2 || ret >= 0xffffffff) { printf("Invalid CID: '%s'\n", value); args->flags |= F_ERR; } else args->net.cid = ret; } static inline void assign_interface(fence_virt_args_t *args, struct arg_info *arg, char *value) { int ret; if (!value) return; ret = if_nametoindex(value); if (ret <= 0) { printf("Invalid interface: %s: %s\n", value, strerror(errno)); args->net.ifindex = 0; } args->net.ifindex = ret; } static inline void assign_retrans(fence_virt_args_t *args, struct arg_info *arg, char *value) { char *p; int ret; if (!value) return; ret = strtol(value, &p, 0); if (ret <= 0 || *p != '\0') { printf("Invalid retransmit time: '%s'\n", value); args->flags |= F_ERR; } else args->retr_time = ret; } static inline void assign_hash(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (!strcasecmp(value, "none")) { args->net.hash = HASH_NONE; } else if (!strcasecmp(value, "sha1")) { args->net.hash = HASH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->net.hash = HASH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->net.hash = HASH_SHA512; } else { printf("Unsupported hash: %s\n", value); args->flags |= F_ERR; } } static inline void assign_auth(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (!strcasecmp(value, "none")) { args->net.auth = AUTH_NONE; } else if (!strcasecmp(value, "sha1")) { args->net.auth = AUTH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->net.auth = AUTH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->net.auth = AUTH_SHA512; } else { printf("Unsupported auth type: %s\n", value); args->flags |= F_ERR; } } static inline void assign_key(fence_virt_args_t *args, struct arg_info *arg, char *value) { struct stat st; if (!value) return; if (args->net.key_file) free(args->net.key_file); args->net.key_file = strdup(value); if (stat(value, &st) == -1) { printf("Invalid key file: '%s' (%s)\n", value, strerror(errno)); args->flags |= F_ERR; } } static inline void assign_op(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; if (!strcasecmp(value, "null")) { args->op = FENCE_NULL; } else if (!strcasecmp(value, "on")) { args->op = FENCE_ON; } else if (!strcasecmp(value, "off")) { args->op = FENCE_OFF; } else if (!strcasecmp(value, "reboot")) { args->op = FENCE_REBOOT; } else if (!strcasecmp(value, "status")) { args->op = FENCE_STATUS; } else if (!strcasecmp(value, "monitor")) { args->op = FENCE_DEVSTATUS; } else if (!strcasecmp(value, "list") || !strcasecmp(value, "list-status")) { args->op = FENCE_HOSTLIST; } else if (!strcasecmp(value, "metadata")) { args->op = FENCE_METADATA; } else if (!strcasecmp(value, "validate-all")) { args->op = FENCE_VALIDATEALL; } else { printf("Unsupported operation: %s\n", value); args->flags |= F_ERR; } } static inline void assign_device(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; args->serial.device = strdup(value); } static inline void assign_params(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) return; args->serial.speed = strdup(value); } static inline void assign_domain(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (args->domain) { printf("Domain/UUID may not be specified more than once\n"); args->flags |= F_ERR; return; } if (!value) return; args->domain = strdup(value); if (strlen(value) <= 0) { printf("Invalid domain name\n"); args->flags |= F_ERR; } if (strlen(value) >= MAX_DOMAINNAME_LENGTH) { errno = ENAMETOOLONG; printf("Invalid domain name: '%s' (%s)\n", value, strerror(errno)); args->flags |= F_ERR; } } static inline void assign_uuid_lookup(fence_virt_args_t *args, struct arg_info *arg, char *value) { if (!value) { /* GNU getopt sets optarg to NULL for options w/o a param We rely on this here... */ args->flags |= F_USE_UUID; return; } args->flags |= ( !!atoi(value) ? F_USE_UUID : 0); } static inline void assign_timeout(fence_virt_args_t *args, struct arg_info *arg, char *value) { char *p; int ret; if (!value) return; ret = strtol(value, &p, 0); if (ret <= 0 || *p != '\0') { printf("Invalid timeout: '%s'\n", value); args->flags |= F_ERR; } else args->timeout = ret; } static inline void assign_delay(fence_virt_args_t *args, struct arg_info *arg, char *value) { char *p; int ret; if (!value) return; ret = strtol(value, &p, 0); if (ret < 0 || *p != '\0') { printf("Invalid delay: '%s'\n", value); args->flags |= F_ERR; } else args->delay = ret; } static inline void assign_help(fence_virt_args_t *args, struct arg_info *arg, char *value) { args->flags |= F_HELP; } static inline void assign_version(fence_virt_args_t *args, struct arg_info *arg, char *value) { args->flags |= F_VERSION; } static void print_desc_xml(const char *desc) { const char *d; for (d = desc; *d; d++) { switch (*d) { case '<': printf("<"); break; case '>': printf(">"); break; default: printf("%c", *d); } } } /** ALL valid command line and stdin arguments for this fencing agent */ static struct arg_info _arg_info[] = { { '\xff', NULL, "agent", 0, "string", NULL, "Not user serviceable", NULL }, { '\xff', NULL, "self", 0, "string", NULL, "Not user serviceable", NULL }, { '\xff', NULL, "nodename", 0, "string", NULL, "Not user serviceable", NULL }, { 'd', "-d", "debug", 0, "boolean", NULL, "Specify (stdin) or increment (command line) debug level", assign_debug }, { 'i', "-i ", "ip_family", 0, "string", "auto", "IP Family ([auto], ipv4, ipv6)", assign_family }, { 'a', "-a
", "multicast_address", 0, "string", NULL, "Multicast address (default=" IPV4_MCAST_DEFAULT " / " IPV6_MCAST_DEFAULT ")", assign_address }, { 'T', "-T
", "ipaddr", 0, "string", "127.0.0.1", "IP address to connect to in TCP mode (default=" IPV4_TCP_ADDR_DEFAULT " / " IPV6_TCP_ADDR_DEFAULT ")", assign_ip_address }, { 'S', "-S ", "vsock", 0, "int", "2", "vm socket CID to connect to in vsock mode", assign_cid }, { 'A', "-A
", "channel_address", 0, "string", "10.0.2.179", "VM Channel IP address (default=" DEFAULT_CHANNEL_IP ")", assign_channel_address }, { 'p', "-p ", "ipport", 0, "string", "1229", "TCP, Multicast, VMChannel, or VM socket port (default=1229)", assign_port }, { 'I', "-I ", "interface", 0, "string", NULL, "Network interface name to listen on", assign_interface }, { 'r', "-r ", "retrans", 0, "string", "20", "Multicast retransmit time (in 1/10sec; default=20)", assign_retrans }, { 'c', "-c ", "hash", 0, "string", "sha256", "Packet hash strength (none, sha1, [sha256], sha512)", assign_hash }, { 'C', "-C ", "auth", 0, "string", "sha256", "Authentication (none, sha1, [sha256], sha512)", assign_auth }, { 'k', "-k ", "key_file", 0, "string", DEFAULT_KEY_FILE, "Shared key file (default=" DEFAULT_KEY_FILE ")", assign_key }, { 'D', "-D ", "serial_device", 0, "string", NULL, "Serial device (default=" DEFAULT_SERIAL_DEVICE ")", assign_device }, { 'P', "-P ", "serial_params", 0, "string", DEFAULT_SERIAL_SPEED, "Serial Parameters (default=" DEFAULT_SERIAL_SPEED ")", assign_params }, { '\xff', NULL, "option", /* Deprecated */ 0, "string", "reboot", "Fencing option (null, off, on, [reboot], status, list, list-status, monitor, validate-all, metadata)", assign_op }, { 'o', "-o ", "action", 0, "string", "reboot", "Fencing action (null, off, on, [reboot], status, list, list-status, monitor, validate-all, metadata)", assign_op }, { 'H', "-H ", "port", 0, "string", NULL, "Virtual Machine (domain name) to fence", assign_domain }, { SCHEMA_COMPAT, NULL, "domain", 0, "string", NULL, "Virtual Machine (domain name) to fence (deprecated; use port)", assign_domain }, { 'u', "-u", "use_uuid", 0, "string", "0", "Treat [domain] as UUID instead of domain name. This is provided for compatibility with older fence_xvmd installations.", assign_uuid_lookup }, { 't', "-t ", "timeout", 0, "string", "30", "Fencing timeout (in seconds; default=30)", assign_timeout }, { 'h', "-h", NULL, 0, "boolean", "0", "Help", assign_help }, { '?', "-?", NULL, 0, "boolean", "0", "Help (alternate)", assign_help }, { 'w', "-w ", "delay", 0, "string", "0", "Fencing delay (in seconds; default=0)", assign_delay }, { 'V', "-V", NULL, 0, "boolean", "0", "Display version and exit", assign_version }, /* Terminator */ { 0, NULL, NULL, 0, NULL, NULL, NULL, NULL } }; struct arg_info * find_arg_by_char(char arg) { int x = 0; for (x = 0; _arg_info[x].opt != 0; x++) { if (_arg_info[x].opt == arg) return &_arg_info[x]; } return NULL; } struct arg_info * find_arg_by_string(char *arg) { int x = 0; for (x = 0; _arg_info[x].opt != 0; x++) { if (!_arg_info[x].stdin_opt) continue; if (!strcasecmp(_arg_info[x].stdin_opt, arg)) return &_arg_info[x]; } return NULL; } /* ============================================================= */ /** Initialize an args structure. @param args Pointer to args structure to initialize. */ void args_init(fence_virt_args_t *args) { args->domain = NULL; //args->uri = NULL; args->op = FENCE_REBOOT; args->net.key_file = strdup(DEFAULT_KEY_FILE); args->net.hash = DEFAULT_HASH; args->net.auth = DEFAULT_AUTH; args->net.addr = NULL; args->net.ipaddr = NULL; args->net.cid = 0; args->net.port = DEFAULT_MCAST_PORT; args->net.ifindex = 0; args->net.family = 0; /* auto */ args->serial.device = NULL; args->serial.speed = strdup(DEFAULT_SERIAL_SPEED); args->serial.address = strdup(DEFAULT_CHANNEL_IP); args->timeout = 30; args->retr_time = 20; args->flags = 0; args->debug = 0; args->delay = 0; } #define _pr_int(piece) printf(" %s = %d\n", #piece, piece) #define _pr_str(piece) printf(" %s = %s\n", #piece, piece) /** Prints out the contents of an args structure for debugging. @param args Pointer to args structure to print out. */ void args_print(fence_virt_args_t *args) { printf("-- args @ %p --\n", args); _pr_str(args->domain); _pr_int(args->op); _pr_int(args->mode); _pr_int(args->debug); _pr_int(args->timeout); _pr_int(args->delay); _pr_int(args->retr_time); _pr_int(args->flags); _pr_str(args->net.addr); _pr_str(args->net.ipaddr); _pr_int(args->net.cid); _pr_str(args->net.key_file); _pr_int(args->net.port); _pr_int(args->net.hash); _pr_int(args->net.auth); _pr_int(args->net.family); _pr_int(args->net.ifindex); _pr_str(args->serial.device); _pr_str(args->serial.speed); _pr_str(args->serial.address); printf("-- end args --\n"); } /** Print out arguments and help information based on what is allowed in the getopt string optstr. @param progname Program name. @param optstr Getopt(3) style options string @param print_stdin 0 = print command line options + description, 1 = print fence-style stdin args + description */ static char * find_rev(const char *start, char *curr, char c) { while (curr > start) { if (*curr == c) return curr; --curr; } return NULL; } static void output_help_text(int arg_width, int help_width, const char *arg, const char *desc) { char out_buf[4096]; char *p, *start; const char *arg_print = arg; int len; memset(out_buf, 0, sizeof(out_buf)); strncpy(out_buf, desc, sizeof(out_buf) - 1); start = out_buf; do { p = NULL; len = strlen(start); if (len > help_width) { p = start + help_width; p = find_rev(start, p, ' '); if (p) { *p = 0; p++; } } printf(" %*.*s %*.*s\n", -arg_width, arg_width, arg_print, -help_width, help_width, start); if (!p) return; if (arg == arg_print) arg_print = " "; start = p; } while(1); } void args_usage(char *progname, const char *optstr, int print_stdin) { int x; struct arg_info *arg; if (!print_stdin) { if (progname) { printf("usage: %s [args]\n", progname); } else { printf("usage: fence_virt [args]\n"); } } for (x = 0; x < strlen(optstr); x++) { arg = find_arg_by_char(optstr[x]); if (!arg) continue; if (print_stdin) { if (arg && arg->stdin_opt) output_help_text(20, 55, arg->stdin_opt, arg->desc); } else { output_help_text(20, 55, arg->opt_desc, arg->desc); } } printf("\n"); } void args_metadata(char *progname, const char *optstr) { int x; struct arg_info *arg; printf("\n"); printf("\n", basename(progname)); printf("%s is an I/O Fencing agent which can be used with" "virtual machines.\n", basename(progname)); printf("\n"); for (x = 0; x < strlen(optstr); x++) { arg = find_arg_by_char(optstr[x]); if (!arg) continue; if (!arg->stdin_opt) continue; printf("\t\n",arg->stdin_opt); printf("\t\t\n",arg->opt); if (arg->default_value) { printf("\t\t\n", arg->content_type, arg->default_value); } else { printf("\t\t\n", arg->content_type); } printf("\t\t"); print_desc_xml(arg->desc); printf("\n"); printf("\t\n"); } for (x = 0; _arg_info[x].opt != 0; x++) { if (_arg_info[x].opt != SCHEMA_COMPAT) continue; arg = &_arg_info[x]; printf("\t\n",arg->stdin_opt); printf("\t\t\n"); if (arg->default_value) { printf("\t\t\n", arg->content_type, arg->default_value); } else { printf("\t\t\n", arg->content_type); } printf("\t\t"); print_desc_xml(arg->desc); printf("\n"); printf("\t\n"); } printf("\n"); printf("\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\t\n"); printf("\n"); printf("\n"); } /** Remove leading and trailing whitespace from a line of text. @param line Line to clean up @param linelen Max size of line @return 0 on success, -1 on failure */ int cleanup(char *line, size_t linelen) { char *p; int x; /* Remove leading whitespace. */ p = line; for (x = 0; x <= linelen; x++) { switch (line[x]) { case '\t': case ' ': break; case '\n': case '\r': return -1; default: goto eol; } } eol: /* Move the remainder down by as many whitespace chars as we chewed up */ if (x) memmove(p, &line[x], linelen-x); /* Remove trailing whitespace. */ for (x=0; x <= linelen; x++) { switch(line[x]) { case '\t': case ' ': case '\r': case '\n': line[x] = 0; case 0: /* End of line */ return 0; } } return -1; } /** Parse args from stdin and assign to the specified args structure. @param optstr Command line option string in getopt(3) format @param args Args structure to fill in. */ void args_get_stdin(const char *optstr, fence_virt_args_t *args) { char in[256]; int line = 0; char *name, *val; struct arg_info *arg; while (fgets(in, sizeof(in), stdin)) { ++line; if (in[0] == '#') continue; if (cleanup(in, sizeof(in)) == -1) continue; name = in; if ((val = strchr(in, '='))) { *val = 0; ++val; } arg = find_arg_by_string(name); if (!arg || (arg->opt != '\xff' && arg->opt != SCHEMA_COMPAT && !strchr(optstr, arg->opt))) { fprintf(stderr, "parse warning: " "illegal variable '%s' on line %d\n", name, line); args->flags |= F_ERR; continue; } if (arg->assign) arg->assign(args, arg, val); } } /** Parse args from stdin and assign to the specified args structure. @param optstr Command line option string in getopt(3) format @param args Args structure to fill in. */ void args_get_getopt(int argc, char **argv, const char *optstr, fence_virt_args_t *args) { int opt; struct arg_info *arg; while ((opt = getopt(argc, argv, optstr)) != EOF) { arg = find_arg_by_char(opt); if (!arg) { args->flags |= F_ERR; continue; } if (arg->assign) arg->assign(args, arg, optarg); } } void args_finalize(fence_virt_args_t *args) { char *addr = NULL; if (!args->net.addr) { switch(args->net.family) { case 0: case PF_INET: addr = IPV4_MCAST_DEFAULT; break; case PF_INET6: addr = IPV6_MCAST_DEFAULT; break; default: args->flags |= F_ERR; break; } } if (!args->net.addr) args->net.addr = addr; if (!args->net.addr) { printf("No multicast address available\n"); args->flags |= F_ERR; } if (!args->net.addr) return; if (args->net.family) return; /* Set family */ if (strchr(args->net.addr, ':')) args->net.family = PF_INET6; if (strchr(args->net.addr, '.')) args->net.family = PF_INET; if (!args->net.family) { printf("Could not determine address family\n"); args->flags |= F_ERR; } } fence-virt-1.0.0/client/serial.c000066400000000000000000000127161356743026700164770ustar00rootroot00000000000000/* * Copyright (C) 2002-2003, 2009 Red Hat, Inc. * * License: GPLv2+ * * Written by Lon Hohberger * * Serial client for fence_virt (incomplete, but * a good start) * * Based on: * Ubersimpledumbterminal "ser" version 1.0.3 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int char_to_speed(const char *speed) { if (!speed || !strlen(speed)) return B9600; if (!strcmp(speed,"2400")) return B2400; if (!strcmp(speed,"9600")) return B9600; if (!strcmp(speed,"19200")) return B19200; if (!strcmp(speed,"38400")) return B38400; if (!strcmp(speed,"57600")) return B57600; if (!strcmp(speed,"115200")) return B115200; return -1; } static int char_to_flags(const char *param) { int db_f = CS8, par_f = 0, sb_f = 0, x; if (!param || !strlen(param)) return (db_f | par_f | sb_f); if (strlen(param) < 3) { errno = EINVAL; return -1; } for (x = 0; x < 3; x++) { switch (param[x]) { case '5': db_f = CS5; break; case '6': db_f = CS6; break; case '7': db_f = CS7; break; case '8': db_f = CS8; break; case 'n': case 'N': par_f = 0; break; case 'e': case 'E': par_f = PARENB; break; case 'o': case 'O': par_f = PARENB | PARODD; break; case '1': sb_f = 0; break; case '2': sb_f = CSTOPB; break; default: printf("Fail: %c\n", param[x]); errno = EINVAL; return -1; } } return (db_f | par_f | sb_f); } static int open_port(char *file, char *cspeed, char *cparam) { struct termios ti; int fd, speed = B115200, flags = 0; struct flock lock; if ((speed = char_to_speed(cspeed)) == -1) { errno = EINVAL; return -1; } if ((flags = char_to_flags(cparam)) == -1) { errno = EINVAL; return -1; } if ((fd = open(file, O_RDWR | O_EXCL)) == -1) { perror("open"); return -1; } memset(&lock,0,sizeof(lock)); lock.l_type = F_WRLCK; if (fcntl(fd, F_SETLK, &lock) == -1) { perror("Failed to lock serial port"); close(fd); return -1; } memset(&ti, 0, sizeof(ti)); ti.c_cflag = (speed | CLOCAL | CRTSCTS | CREAD | flags); if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("tcsetattr"); close(fd); return -1; } (void) tcflush(fd, TCIOFLUSH); return fd; } static void hangup(int fd, int delay) { unsigned int bits; if (ioctl(fd, TIOCMGET, &bits)) { perror("ioctl1"); return; } bits &= ~(TIOCM_DTR | TIOCM_CTS | TIOCM_RTS | TIOCM_DSR | TIOCM_CD); if (ioctl(fd, TIOCMSET, &bits)) { perror("ioctl2"); return; } usleep(delay); bits |= (TIOCM_DTR | TIOCM_CTS | TIOCM_RTS | TIOCM_DSR | TIOCM_CD); if (ioctl(fd, TIOCMSET, &bits)) { perror("ioctl3"); return; } } void do_read_hostlist(int fd, int timeout); int wait_for(int fd, const char *pattern, size_t size, struct timeval *tout) { char *pos = (char *)pattern; char c; int n; struct timeval tv; size_t remain = size; if (tout) { memcpy(&tv, tout, sizeof(tv)); tout = &tv; } while (remain) { n = _read_retry(fd, &c, 1, &tv); if (n < 1) return -1; if (c == *pos) { ++pos; --remain; } else { pos = (char *)pattern; remain = size; } } return 0; } int serial_fence_virt(fence_virt_args_t *args) { struct in_addr ina; struct in6_addr in6a; serial_req_t req; int fd, ret; char speed[32], *flags = NULL; struct timeval tv; serial_resp_t resp; if (args->serial.device) { strncpy(speed, args->serial.speed, sizeof(speed) - 1); //printf("Port: %s Speed: %s\n", args->serial.device, speed); if ((flags = strchr(speed, ','))) { *flags = 0; flags++; } fd = open_port(args->serial.device, speed, flags); if (fd == -1) { perror("open_port"); return -1; } hangup(fd, 300000); } else { fd = -1; if (inet_pton(PF_INET, args->serial.address, &ina)) { fd = ipv4_connect(&ina, args->net.port, 3); } else if (inet_pton(PF_INET6, args->serial.address, &in6a)) { fd = ipv6_connect(&in6a, args->net.port, 3); } if (fd < 0) { perror("vmchannel connect"); printf("Failed to connect to %s:%d\n", args->serial.address, args->net.port); return -1; } } memset(&req, 0, sizeof(req)); req.magic = SERIAL_MAGIC; req.request = (uint8_t)args->op; gettimeofday(&tv, NULL); req.seqno = (int)tv.tv_usec; if (args->domain) strncpy((char *)req.domain, args->domain, sizeof(req.domain) - 1); tv.tv_sec = 3; tv.tv_usec = 0; swab_serial_req_t(&req); ret = _write_retry(fd, &req, sizeof(req), &tv); if (ret < sizeof(req)) { if (ret < 0) { close(fd); return ret; } printf("Failed to send request\n"); } tv.tv_sec = args->timeout; tv.tv_usec = 0; resp.magic = SERIAL_MAGIC; do { if (wait_for(fd, (const char *)&resp.magic, sizeof(resp.magic), &tv) == 0) { ret = _read_retry(fd, &resp.response, sizeof(resp.response), &tv); } else { /* The other end died or closed the connection */ close(fd); return -1; } swab_serial_resp_t(&resp); } while(resp.magic != SERIAL_MAGIC && (tv.tv_sec || tv.tv_usec)); if (resp.magic != SERIAL_MAGIC) { close(fd); return -1; } ret = resp.response; if (resp.response == RESP_HOSTLIST) /* hostlist */ { /* ok read hostlist */ do_read_hostlist(fd, args->timeout); ret = 0; } close(fd); return ret; } fence-virt-1.0.0/client/tcp.c000066400000000000000000000103201356743026700157730ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2012 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "tcp.h" #include "debug.h" #include "fdops.h" void do_read_hostlist(int fd, int timeout); static int tcp_exchange(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { fd_set rfds; struct timeval tv; char ret = 1; /* Ok, we're connected */ dbg_printf(3, "Issuing TCP challenge\n"); if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) { /* Challenge failed */ printf("Invalid response to challenge\n"); return 1; } /* Now they'll send us one, so we need to respond here */ dbg_printf(3, "Responding to TCP challenge\n"); if (sock_response(fd, auth, key, key_len, timeout) <= 0) { printf("Invalid response to challenge\n"); return 1; } dbg_printf(2, "TCP Exchange + Authentication done... \n"); FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = 1; dbg_printf(3, "Waiting for return value from fence_virtd host\n"); if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) return -1; /* Read return code */ if (_read_retry(fd, &ret, 1, &tv) < 0) ret = 1; if (ret == (char)RESP_HOSTLIST) /* hostlist */ { do_read_hostlist(fd, timeout); ret = 0; } return ret; } int tcp_fence_virt(fence_virt_args_t *args) { char key[MAX_KEY_LEN]; struct timeval tv; int key_len = 0, fd = -1; int ret; struct in_addr ina; struct in6_addr in6a; fence_req_t freq; /* Initialize NSS; required to do hashing, as silly as that sounds... */ if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) { key_len = read_key_file(args->net.key_file, key, sizeof(key)); if (key_len < 0) { printf("Could not read %s; trying without " "authentication\n", args->net.key_file); args->net.auth = AUTH_NONE; args->net.hash = HASH_NONE; key_len = 0; } } /* Same wire protocol as fence_xvm */ memset(&freq, 0, sizeof(freq)); if (args->domain && strlen((char *)args->domain)) strncpy((char *)freq.domain, args->domain, sizeof(freq.domain) - 1); freq.request = args->op; freq.hashtype = args->net.hash; freq.flags = 0; if (args->flags & F_USE_UUID) freq.flags |= RF_UUID; gettimeofday(&tv, NULL); freq.seqno = (uint32_t) tv.tv_usec; sign_request(&freq, key, key_len); /* XXX fixme */ if (inet_pton(PF_INET, args->net.ipaddr, &ina)) { fd = ipv4_connect(&ina, args->net.port, 3); } else if (inet_pton(PF_INET6, args->net.ipaddr, &in6a)) { fd = ipv6_connect(&in6a, args->net.port, 3); } if (fd < 0) { printf("Unable to connect to fence_virtd host %s:%d %s\n", args->net.ipaddr, args->net.port, strerror(errno)); return 1; } ret = _write_retry(fd, &freq, sizeof(freq), NULL); if (ret != sizeof(freq)) { perror("write"); close(fd); return 1; } switch (args->net.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: ret = tcp_exchange(fd, args->net.auth, key, key_len, args->timeout); close(fd); return ret; break; /* case AUTH_X509: return ssl_exchange(...); */ default: dbg_printf(3, "Unknown auth type: %d\n", args->net.auth); close(fd); return 1; } close(fd); return 1; } fence-virt-1.0.0/client/vsock.c000066400000000000000000000104561356743026700163440ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "debug.h" #include "fdops.h" void do_read_hostlist(int fd, int timeout); static int sock_exchange(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { fd_set rfds; struct timeval tv; char ret = 1; /* Ok, we're connected */ dbg_printf(3, "Issuing challenge\n"); if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) { /* Challenge failed */ printf("Invalid response to challenge\n"); return 1; } /* Now they'll send us one, so we need to respond here */ dbg_printf(3, "Responding to challenge\n"); if (sock_response(fd, auth, key, key_len, timeout) <= 0) { printf("Invalid response to challenge\n"); return 1; } dbg_printf(2, "vsock Exchange + Authentication done... \n"); FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = 1; dbg_printf(3, "Waiting for return value from fence_virtd host\n"); if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) return -1; /* Read return code */ if (_read_retry(fd, &ret, 1, &tv) < 0) ret = 1; if (ret == (char)RESP_HOSTLIST) /* hostlist */ { do_read_hostlist(fd, timeout); ret = 0; } return ret; } int vsock_fence_virt(fence_virt_args_t *args) { char key[MAX_KEY_LEN]; struct timeval tv; int key_len = 0, fd = -1; int ret; struct sockaddr_vm svm; fence_req_t freq; /* Initialize NSS; required to do hashing, as silly as that sounds... */ if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) { key_len = read_key_file(args->net.key_file, key, sizeof(key)); if (key_len < 0) { printf("Could not read %s; trying without " "authentication\n", args->net.key_file); args->net.auth = AUTH_NONE; args->net.hash = HASH_NONE; key_len = 0; } } /* Same wire protocol as fence_xvm */ memset(&freq, 0, sizeof(freq)); if (args->domain && strlen((char *)args->domain)) strncpy((char *)freq.domain, args->domain, sizeof(freq.domain) - 1); freq.request = args->op; freq.hashtype = args->net.hash; freq.flags = 0; if (args->flags & F_USE_UUID) freq.flags |= RF_UUID; gettimeofday(&tv, NULL); freq.seqno = (uint32_t) tv.tv_usec; sign_request(&freq, key, key_len); fd = socket(PF_VSOCK, SOCK_STREAM, 0); if (fd < 0) { printf("Unable to create vsock: %s", strerror(errno)); return 1; } memset(&svm, 0, sizeof(svm)); svm.svm_family = AF_VSOCK; svm.svm_cid = args->net.cid; svm.svm_port = args->net.port; if (connect(fd, (struct sockaddr *) &svm, sizeof(svm)) < 0) { printf("Unable to connect to fence_virtd host %d:%d %s\n", args->net.cid, args->net.port, strerror(errno)); close(fd); return 1; } ret = _write_retry(fd, &freq, sizeof(freq), NULL); if (ret != sizeof(freq)) { perror("write"); close(fd); return 1; } switch (args->net.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: ret = sock_exchange(fd, args->net.auth, key, key_len, args->timeout); close(fd); return ret; break; /* case AUTH_X509: return ssl_exchange(...); */ default: dbg_printf(3, "Unknown auth type: %d\n", args->net.auth); close(fd); return 1; } close(fd); return 1; } fence-virt-1.0.0/common/000077500000000000000000000000001356743026700150575ustar00rootroot00000000000000fence-virt-1.0.0/common/Makefile.am000066400000000000000000000015751356743026700171230ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in noinst_LTLIBRARIES = libfence_virt.la libfence_virt_la_SOURCES = mcast.c ip_lookup.c simple_auth.c tcp.c \ debug.c fdops.c log.c libfence_virt_la_CFLAGS = $(AM_CFLAGS) $(nss_CFLAGS) libfence_virt_la_LDFLAGS = $(AM_LDFLAGS) $(COMMON_LDFLAGS) libfence_virt_la_LIBADD = $(nss_LIBS) fence-virt-1.0.0/common/bcast.c000066400000000000000000000176641356743026700163350ustar00rootroot00000000000000/* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "bcast.h" #include "debug.h" LOGSYS_DECLARE_SUBSYS ("XVM", SYSLOGLEVEL); /** Sets up a multicast receive socket */ int ipv4_bcast_recv_sk(char *addr, int port) { int sock, val; struct sockaddr_in sin; /* Store broadcast address */ memset(&sin, 0, sizeof(sin)); sin.sin_family = PF_INET; sin.sin_port = htons(port); if (inet_pton(PF_INET, addr, (void *)&sin.sin_addr.s_addr) < 0) { log_printf(LOG_ERR, "Invalid broadcast address: %s\n", addr); return -1; } dbg_printf(4, "Setting up ipv4 broadcast receive (%s:%d)\n", addr, port); sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { log_printf(LOG_ERR, "socket: %s\n", strerror(errno)); close(sock); sock = -1; return 1; } val = 1; if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, 1, sizeof(val)) < 0) { log_printf(LOG_ERR, "setsockopt: %s\n", strerror(errno)); close(sock); return 1; } /* * Bind broadcast address */ if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { printf("bind failed: %s\n", strerror(errno)); close(sock); return -1; } dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Set up multicast send socket */ int ipv4_bcast_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, socklen_t tgt_len, int ttl) { int val; struct ip_mreq mreq; struct sockaddr_in mcast; struct sockaddr_in src; int sock; if (tgt_len < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; } /* Store multicast address */ mcast.sin_family = PF_INET; mcast.sin_port = htons(port); if (inet_pton(PF_INET, addr, (void *)&mcast.sin_addr.s_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr; /* Store sending address */ src.sin_family = PF_INET; src.sin_port = htons(port); if (inet_pton(PF_INET, send_addr, (void *)&src.sin_addr.s_addr) < 0) { printf("Invalid source address: %s\n", send_addr); return -1; } mreq.imr_interface.s_addr = src.sin_addr.s_addr; /************************* * SET UP MULTICAST SEND * *************************/ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port); sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group (pass 1)\n"); if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast membership to transmit " "socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group (pass 2)\n"); if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr, sizeof(src.sin_addr)) == -1) { printf("Failed to bind multicast transmit socket to " "%s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * set time to live to 2 hops. */ dbg_printf(4, "Setting TTL to %d for fd%d\n", ttl, sock); val = ttl; if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val, sizeof(val))) printf("warning: setting TTL failed %s\n", strerror(errno)); memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in)); dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Sets up a multicast receive (ipv6) socket */ int ipv6_recv_sk(char *addr, int port) { int sock, val; struct ipv6_mreq mreq; struct sockaddr_in6 sin; memset(&mreq, 0, sizeof(mreq)); memset(&sin, 0, sizeof(sin)); sin.sin6_family = PF_INET6; sin.sin6_port = htons(port); if (inet_pton(PF_INET6, addr, (void *)&sin.sin6_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr, sizeof(struct in6_addr)); /******************************** * SET UP MULTICAST RECV SOCKET * ********************************/ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port); sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { printf("socket: %s\n", strerror(errno)); close(sock); sock = -1; return 1; } /* * When using Multicast, bind to the LOCAL address, not the MULTICAST * address. */ memset(&sin, 0, sizeof(sin)); sin.sin6_family = PF_INET6; sin.sin6_port = htons(port); sin.sin6_addr = in6addr_any; if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)) < 0) { printf("bind failed: %s\n", strerror(errno)); close(sock); return -1; } dbg_printf(4, "Disabling IP Multicast loopback\n"); val = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) { printf("Failed to disable multicast loopback\n"); close(sock); return -1; } /* * Join multicast group */ dbg_printf(4, "Joining IP Multicast group\n"); if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast to socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Set up ipv6 multicast send socket */ int ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, socklen_t tgt_len, int ttl) { int val; struct ipv6_mreq mreq; struct sockaddr_in6 mcast; struct sockaddr_in6 src; int sock; if (tgt_len < sizeof(struct sockaddr_in6)) { errno = EINVAL; return -1; } memset(&mreq, 0, sizeof(mreq)); /* Store multicast address */ mcast.sin6_family = PF_INET6; mcast.sin6_port = htons(port); if (inet_pton(PF_INET6, addr, (void *)&mcast.sin6_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr, sizeof(struct in6_addr)); /* Store sending address */ src.sin6_family = PF_INET6; src.sin6_port = htons(port); if (inet_pton(PF_INET6, send_addr, (void *)&src.sin6_addr) < 0) { printf("Invalid source address: %s\n", send_addr); return -1; } /************************* * SET UP MULTICAST SEND * *************************/ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port); sock = socket(PF_INET6, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return -1; } dbg_printf(4, "Disabling IP Multicast loopback\n"); val = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) { printf("Failed to disable multicast loopback\n"); close(sock); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group\n"); if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast membership to transmit " "socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * Join Multicast group (part 2) */ /* if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr, sizeof(src.sin6_addr)) == -1) { printf("Failed to bind multicast transmit socket to " "%s: %s\n", addr, strerror(errno)); close(sock); return -1; } */ /* * set time to live to 2 hops. */ val = ttl; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val))) printf("warning: setting TTL failed %s\n", strerror(errno)); memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6)); dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } fence-virt-1.0.0/common/debug.c000066400000000000000000000017051356743026700163140ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include "debug.h" static int _debug = 0; void dset(int threshold) { _debug = threshold; dbg_printf(3, "Debugging threshold is now %d\n", threshold); } int dget(void) { return _debug; } fence-virt-1.0.0/common/fdops.c000066400000000000000000000111711356743026700163370ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2002-2003 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file * Wrapper functions around read/write/select to retry in the event * of interrupts. */ #include "config.h" #include #include #include #include /** * This is a wrapper around select which will retry in the case we receive * EINTR. This is necessary for _read_retry, since it wouldn't make sense * to have _read_retry terminate if and only if two EINTRs were received * in a row - one during the read() call, one during the select call... * * See select(2) for description of parameters. */ int _select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds, struct timeval *timeout) { int rv; while (1) { rv = select(fdmax, rfds, wfds, xfds, timeout); if (rv == -1) { /* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */ if (errno == EINTR || errno == EAGAIN || errno == ENOMEM) continue; } return rv; } } /** * Retries a write in the event of a non-blocked interrupt signal. * * @param fd File descriptor to which we are writing. * @param buf Data buffer to send. * @param count Number of bytes in buf to send. * @param timeout (struct timeval) telling us how long we should retry. * @return The number of bytes written to the file descriptor, * or -1 on error (with errno set appropriately). */ ssize_t _write_retry(int fd, void *buf, int count, struct timeval * timeout) { int n, total = 0, remain = count, rv = 0; fd_set wfds, xfds; while (total < count) { /* Create the write FD set of 1... */ FD_ZERO(&wfds); FD_SET(fd, &wfds); FD_ZERO(&xfds); FD_SET(fd, &xfds); /* wait for the fd to be available for writing */ rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout); if (rv == -1) return -1; else if (rv == 0) { errno = ETIMEDOUT; return -1; } if (FD_ISSET(fd, &xfds)) { errno = EPIPE; return -1; } /* * Attempt to write to fd */ n = write(fd, buf + (off_t) total, remain); /* * When we know our fd was select()ed and we receive 0 bytes * when we write, the fd was closed. */ if ((n == 0) && (rv == 1)) { errno = EPIPE; return -1; } if (n == -1) { if ((errno == EAGAIN) || (errno == EINTR)) { /* * Not ready? */ continue; } /* Other errors: EIO, EINVAL, etc */ return -1; } total += n; remain -= n; } return total; } /** * Retry reads until we (a) time out or (b) get our data. Of course, if * timeout is NULL, it'll wait forever. * * @param sockfd File descriptor we want to read from. * @param buf Preallocated buffer into which we will read data. * @param count Number of bytes to read. * @param timeout (struct timeval) describing how long we should retry. * @return The number of bytes read on success, or -1 on failure. Note that we will always return (count) or (-1). */ ssize_t _read_retry(int sockfd, void *buf, int count, struct timeval * timeout) { int n, total = 0, remain = count, rv = 0; fd_set rfds, xfds; while (total < count) { FD_ZERO(&rfds); FD_SET(sockfd, &rfds); FD_ZERO(&xfds); FD_SET(sockfd, &xfds); /* * Select on the socket, in case it closes while we're not * looking... */ rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout); if (rv == -1) return -1; else if (rv == 0) { errno = ETIMEDOUT; return -1; } if (FD_ISSET(sockfd, &xfds)) { errno = EPIPE; return -1; } /* * Attempt to read off the socket */ n = read(sockfd, buf + (off_t) total, remain); /* * When we know our socket was select()ed and we receive 0 bytes * when we read, the socket was closed. */ if ((n == 0) && (rv == 1)) { errno = EPIPE; return -1; } if (n == -1) { if ((errno == EAGAIN) || (errno == EINTR)) { /* * Not ready? Wait for data to become available */ continue; } /* Other errors: EPIPE, EINVAL, etc */ return -1; } total += n; remain -= n; } return total; } fence-virt-1.0.0/common/ip_lookup.c000066400000000000000000000155651356743026700172400ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2004, 2006 The Magma Cluster API Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The Magma Cluster API Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /** @file * Build lists of IPs on the system, excepting loopback ipv6 link-local */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "ip_lookup.h" #include "debug.h" static int send_addr_dump(int fd, int family) { struct nlmsghdr *nh; struct rtgenmsg *g; char buf[256]; struct sockaddr_nl addr; memset(&addr,0,sizeof(addr)); addr.nl_family = PF_NETLINK; memset(buf, 0, sizeof(buf)); nh = (struct nlmsghdr *)buf; g = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr)); nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); nh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; nh->nlmsg_type = RTM_GETADDR; g->rtgen_family = family; return sendto(fd, buf, nh->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); } static int add_ip(ip_list_t *ipl, char *ipaddr, char family) { ip_addr_t *ipa; if (family == PF_INET6) { /* Avoid loopback */ if (!strcmp(ipaddr, "::1")) return -1; /* Avoid link-local addresses */ if (!strncmp(ipaddr, "fe80", 4)) return -1; if (!strncmp(ipaddr, "fe90", 4)) return -1; if (!strncmp(ipaddr, "fea0", 4)) return -1; if (!strncmp(ipaddr, "feb0", 4)) return -1; } ipa = calloc(1, sizeof(*ipa)); if (!ipa) return -1; ipa->ipa_family = family; ipa->ipa_address = strdup(ipaddr); dbg_printf(4, "Adding IP %s to list (family %d)\n", ipaddr, family); TAILQ_INSERT_TAIL(ipl, ipa, ipa_entries); return 0; } static int add_ip_addresses(int family, ip_list_t *ipl) { /* List ipv4 addresses */ struct nlmsghdr *nh; struct ifaddrmsg *ifa; struct rtattr *rta, *nrta; struct nlmsgerr *err; char buf[10240]; char outbuf[256]; int x, fd, len; dbg_printf(5, "Connecting to Netlink...\n"); fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { perror("socket"); exit(1); } dbg_printf(5, "Sending address dump request\n"); send_addr_dump(fd, family); memset(buf, 0, sizeof(buf)); dbg_printf(5, "Waiting for response\n"); x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0); if (x < 0) { perror("recvfrom"); close(fd); return -1; } dbg_printf(5, "Received %d bytes\n", x); nh = (struct nlmsghdr *)buf; while (NLMSG_OK(nh, x)) { switch(nh->nlmsg_type) { case NLMSG_DONE: close(fd); return 0; case NLMSG_ERROR: err = (struct nlmsgerr*)NLMSG_DATA(nh); if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { fprintf(stderr, "ERROR truncated"); } else { errno = -err->error; perror("RTNETLINK answers"); } close(fd); return -1; case RTM_NEWADDR: break; default: nh = NLMSG_NEXT(nh, x); continue; } /* RTM_NEWADDR */ len = NLMSG_PAYLOAD(nh,0); ifa = NLMSG_DATA(nh); /* Make sure we got the type we expect back */ if (ifa->ifa_family != family) { nh = NLMSG_NEXT(nh, x); continue; } rta = (struct rtattr *)((void *)ifa + sizeof(*ifa)); len -= sizeof(*ifa); do { /* Make sure we've got a valid rtaddr field */ if (!RTA_OK(rta, len)) { dbg_printf(5, "!RTA_OK(rta, len)\n"); break; } if (rta->rta_type == IFA_ADDRESS) { inet_ntop(family, RTA_DATA(rta), outbuf, sizeof(outbuf) ); add_ip(ipl, outbuf, family); } if (rta->rta_type == IFA_LABEL) { dbg_printf(5, "Skipping label: %s\n", (char *)RTA_DATA(rta)); } nrta = RTA_NEXT(rta, len); if (!nrta) break; len -= ((void *)nrta - (void *)rta); rta = nrta; } while (RTA_OK(rta, len)); nh = NLMSG_NEXT(nh, x); } dbg_printf(5, "Closing Netlink connection\n"); close(fd); return 0; } int ip_search(ip_list_t *ipl, char *ip_name) { ip_addr_t *ipa; dbg_printf(5, "Looking for IP address %s in IP list %p...", ip_name, ipl); ipa = ipl->tqh_first; for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) { if (!strcmp(ip_name, ipa->ipa_address)) { dbg_printf(4,"Found\n"); return 0; } } dbg_printf(5, "Not found\n"); return 1; } int ip_free_list(ip_list_t *ipl) { ip_addr_t *ipa; dbg_printf(5, "Tearing down IP list @ %p\n", ipl); while ((ipa = ipl->tqh_first)) { TAILQ_REMOVE(ipl, ipa, ipa_entries); free(ipa->ipa_address); free(ipa); } return 0; } int ip_build_list(ip_list_t *ipl) { dbg_printf(5, "Build IP address list\n"); TAILQ_INIT(ipl); if (add_ip_addresses(PF_INET6, ipl) < 0) { ip_free_list(ipl); return -1; } if (add_ip_addresses(PF_INET, ipl) < 0) { ip_free_list(ipl); return -1; } return 0; } /** Look up the interface name which corresponds to the given hostname and return the list of matching attrinfo structures. We do this by looking up all the possible physical and virtual network interfaces on the machine and checking the hostname/IP mappings for each active IP address incurred. @param nodename Interface name @param ret_ai Structure pointer to allocate & return. @return -1 on failure or 0 on success. */ int ip_lookup(char *nodename, struct addrinfo **ret_ai) { char ip_name[256]; struct addrinfo *ai = NULL; struct addrinfo *n; void *p; ip_list_t ipl; int ret = -1; dbg_printf(5, "Looking for IP matching %s\n", nodename); /* Build list of IP addresses configured locally */ if (ip_build_list(&ipl) < 0) return -1; /* Get list of addresses for the host-name/ip */ if (getaddrinfo(nodename, NULL, NULL, &ai) != 0) return -1; /* Traverse list of addresses for given host-name/ip */ for (n = ai; n; n = n->ai_next) { if (n->ai_family != PF_INET && n->ai_family != PF_INET6) continue; if (n->ai_family == PF_INET) p = &(((struct sockaddr_in *)n->ai_addr)->sin_addr); else p = &(((struct sockaddr_in6 *)n->ai_addr)->sin6_addr); if (!inet_ntop(n->ai_family, p, ip_name, sizeof(ip_name))) continue; /* Search local interfaces for this IP address */ if (ip_search(&ipl, ip_name) != 0) continue; /* Found it */ ret = 0; break; } /* Clean up */ if (!ret_ai) freeaddrinfo(ai); else *ret_ai = ai; ip_free_list(&ipl); return ret; } fence-virt-1.0.0/common/log.c000066400000000000000000000066431356743026700160150ustar00rootroot00000000000000/** * Syslog wrapper that does not block * * Lon Hohberger, 2009 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" struct log_entry { list_head(); char *message; int sev; int bufsz; }; #define MAX_QUEUE_LENGTH 10 #define LOGLEN 256 static struct log_entry *_log_entries = NULL; static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER; static int log_size = 0; static int dropped = 0; static pthread_t thread_id = 0; void __real_syslog(int severity, const char *fmt, ...); static void * _log_thread(void *arg) { struct timeval tv; struct timespec ts; struct log_entry *entry; do { gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec + 10; ts.tv_nsec = tv.tv_usec; pthread_mutex_lock(&log_mutex); while (!(entry = _log_entries)) { if (pthread_cond_timedwait(&log_cond, &log_mutex, &ts) == ETIMEDOUT) goto out; } list_remove(&_log_entries, entry); --log_size; if (log_size < 0) raise(SIGSEGV); pthread_mutex_unlock(&log_mutex); __real_syslog(entry->sev, entry->message); free(entry->message); free(entry); } while (1); out: thread_id = (pthread_t)0; pthread_mutex_unlock(&log_mutex); return NULL; } static int insert_entry(int sev, char *buf, int bufsz) { struct log_entry *lent; pthread_attr_t attrs; lent = malloc(sizeof(*lent)); if (!lent) return -1; lent->sev = sev; lent->message = buf; lent->bufsz = bufsz; pthread_mutex_lock(&log_mutex); if (log_size >= MAX_QUEUE_LENGTH) { free(lent->message); free(lent); ++dropped; lent = (struct log_entry *)(le(_log_entries)->le_prev); lent->sev = LOG_WARNING; snprintf(lent->message, lent->bufsz, "%d message(s) lost due to syslog load\n", dropped + 1); /* Dropped +1 because we overwrote a message to * give the 'dropped' message */ } else { ++log_size; dropped = 0; list_insert(&_log_entries, lent); } if (!thread_id) { pthread_attr_init(&attrs); pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED); if (pthread_create(&thread_id, &attrs, _log_thread, NULL) < 0) thread_id = 0; pthread_mutex_unlock(&log_mutex); } else { pthread_mutex_unlock(&log_mutex); pthread_cond_signal(&log_cond); } return 0; } __attribute__((__format__ (__printf__, 2, 0))) void __wrap_syslog(int severity, const char *fmt, ...) { va_list args; char *logmsg; logmsg = malloc(LOGLEN); if (!logmsg) return; memset(logmsg, 0, LOGLEN); va_start(args, fmt); vsnprintf(logmsg + strlen(logmsg), LOGLEN - strlen(logmsg), fmt, args); va_end(args); insert_entry(severity, logmsg, LOGLEN); return; } void __real_closelog(void); void __wrap_closelog(void) { struct log_entry *lent; int lost = 0; if (thread_id != 0) { pthread_cancel(thread_id); pthread_join(thread_id, NULL); thread_id = 0; } __real_closelog(); while (_log_entries) { ++lost; lent = _log_entries; list_remove(&_log_entries, lent); free(lent->message); free(lent); } #ifdef DEBUG printf("%d lost\n", lost); #endif } #ifdef STANDALONE int main(int argc, char**argv) { int x; for (x = 0; x < 100; x++) { syslog(1, "Yo %d\n", x); } sleep(1); closelog(); return 0; } #endif fence-virt-1.0.0/common/mcast.c000066400000000000000000000226001356743026700163320ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2003, 2004, 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "mcast.h" #include "debug.h" /** Sets up a multicast receive socket */ int ipv4_recv_sk(char *addr, int port, unsigned int ifindex) { int sock; struct ip_mreqn mreq; struct sockaddr_in sin; memset(&mreq, 0, sizeof(mreq)); memset(&sin, 0, sizeof(sin)); /* Store multicast address */ if (inet_pton(PF_INET, addr, (void *)&mreq.imr_multiaddr.s_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } /******************************** * SET UP MULTICAST RECV SOCKET * ********************************/ dbg_printf(4, "Setting up ipv4 multicast receive (%s:%d)\n", addr, port); sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { printf("socket: %s\n", strerror(errno)); sock = -1; return 1; } /* * When using Multicast, bind to the LOCAL address, not the MULTICAST * address. */ sin.sin_family = PF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { printf("bind failed: %s\n", strerror(errno)); close(sock); return -1; } /* * Join multicast group */ /* mreq.imr_multiaddr.s_addr is set above */ if (ifindex == 0) { dbg_printf(4, "Setting mcast addr to INADDR_ANY due to ifindex of 0\n"); mreq.imr_address.s_addr = htonl(INADDR_ANY); } else { mreq.imr_ifindex = ifindex; } dbg_printf(4, "Joining multicast group\n"); if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to bind multicast receive socket to " "%s: %s\n", addr, strerror(errno)); printf("Check network configuration.\n"); close(sock); return -1; } dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Set up multicast send socket */ int ipv4_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, socklen_t tgt_len) { int val; struct ip_mreq mreq; struct sockaddr_in mcast; struct sockaddr_in src; int sock; if (tgt_len < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; } memset(&mcast, 0, sizeof(mcast)); memset(&src, 0, sizeof(src)); /* Store multicast address */ mcast.sin_family = PF_INET; mcast.sin_port = htons(port); if (inet_pton(PF_INET, addr, (void *)&mcast.sin_addr.s_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr; /* Store sending address */ src.sin_family = PF_INET; src.sin_port = htons(port); if (inet_pton(PF_INET, send_addr, (void *)&src.sin_addr.s_addr) < 0) { printf("Invalid source address: %s\n", send_addr); return -1; } mreq.imr_interface.s_addr = src.sin_addr.s_addr; /************************* * SET UP MULTICAST SEND * *************************/ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port); sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group (pass 1)\n"); if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast membership to transmit " "socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group (pass 2)\n"); if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr, sizeof(src.sin_addr)) == -1) { printf("Failed to bind multicast transmit socket to " "%s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * set time to live to 2 hops. */ dbg_printf(4, "Setting TTL to 2 for fd%d\n", sock); val = 2; if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val, sizeof(val))) printf("warning: setting TTL failed %s\n", strerror(errno)); memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in)); dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Sets up a multicast receive (ipv6) socket */ int ipv6_recv_sk(char *addr, int port, unsigned int ifindex) { int sock, val; struct ipv6_mreq mreq; struct sockaddr_in6 sin; memset(&mreq, 0, sizeof(mreq)); memset(&sin, 0, sizeof(sin)); sin.sin6_family = PF_INET6; sin.sin6_port = htons(port); if (inet_pton(PF_INET6, addr, (void *)&sin.sin6_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr, sizeof(struct in6_addr)); mreq.ipv6mr_interface = ifindex; /******************************** * SET UP MULTICAST RECV SOCKET * ********************************/ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port); sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { printf("socket: %s\n", strerror(errno)); sock = -1; return 1; } /* * When using Multicast, bind to the LOCAL address, not the MULTICAST * address. */ memset(&sin, 0, sizeof(sin)); sin.sin6_family = PF_INET6; sin.sin6_port = htons(port); sin.sin6_addr = in6addr_any; if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)) < 0) { printf("bind failed: %s\n", strerror(errno)); close(sock); return -1; } dbg_printf(4, "Disabling IP Multicast loopback\n"); val = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) { printf("Failed to disable multicast loopback\n"); close(sock); return -1; } /* * Join multicast group */ dbg_printf(4, "Joining IP Multicast group\n"); if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast to socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } /** Set up ipv6 multicast send socket */ int ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, socklen_t tgt_len) { int val; struct ipv6_mreq mreq; struct sockaddr_in6 mcast; struct sockaddr_in6 src; int sock; if (tgt_len < sizeof(struct sockaddr_in6)) { errno = EINVAL; return -1; } memset(&mcast, 0, sizeof(mcast)); memset(&src, 0, sizeof(src)); memset(&mreq, 0, sizeof(mreq)); /* Store multicast address */ mcast.sin6_family = PF_INET6; mcast.sin6_port = htons(port); if (inet_pton(PF_INET6, addr, (void *)&mcast.sin6_addr) < 0) { printf("Invalid multicast address: %s\n", addr); return -1; } memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr, sizeof(struct in6_addr)); /* Store sending address */ src.sin6_family = PF_INET6; src.sin6_port = htons(port); if (inet_pton(PF_INET6, send_addr, (void *)&src.sin6_addr) < 0) { printf("Invalid source address: %s\n", send_addr); return -1; } /************************* * SET UP MULTICAST SEND * *************************/ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port); sock = socket(PF_INET6, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return -1; } dbg_printf(4, "Disabling IP Multicast loopback\n"); val = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) { printf("Failed to disable multicast loopback\n"); close(sock); return -1; } /* * Join Multicast group. */ dbg_printf(4, "Joining IP Multicast group\n"); if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { printf("Failed to add multicast membership to transmit " "socket %s: %s\n", addr, strerror(errno)); close(sock); return -1; } /* * Join Multicast group (part 2) */ /* if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr, sizeof(src.sin6_addr)) == -1) { printf("Failed to bind multicast transmit socket to " "%s: %s\n", addr, strerror(errno)); close(sock); return -1; } */ /* * set time to live to 2 hops. */ val = 2; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val))) printf("warning: setting TTL failed %s\n", strerror(errno)); memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6)); dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); return sock; } fence-virt-1.0.0/common/simple_auth.c000066400000000000000000000225411356743026700175410ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "fdops.h" #include "simple_auth.h" #include "debug.h" static void print_hash(unsigned char *hash, size_t hashlen) { int x; for (x = 0; x < hashlen; x++) printf("%02x", (hash[x]&0xff)); } static int sha_sign(fence_req_t *req, void *key, size_t key_len) { unsigned char hash[SHA512_LENGTH]; HASHContext *h; HASH_HashType ht; unsigned int rlen; int devrand; int ret; switch(req->hashtype) { case HASH_SHA1: ht = HASH_AlgSHA1; break; case HASH_SHA256: ht = HASH_AlgSHA256; break; case HASH_SHA512: ht = HASH_AlgSHA512; break; default: dbg_printf(1, "Unknown hash type: %d\n", req->hashtype); return -1; } dbg_printf(4, "Opening /dev/urandom\n"); devrand = open("/dev/urandom", O_RDONLY); if (devrand < 0) { dbg_printf(1, "Error: open: /dev/urandom: %s", strerror(errno)); return -1; } ret = _read_retry(devrand, req->random, sizeof(req->random), NULL); if (ret <= 0) { dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno)); close(devrand); return -1; } close(devrand); memset(hash, 0, sizeof(hash)); h = HASH_Create(ht); if (!h) return -1; HASH_Begin(h); HASH_Update(h, key, key_len); HASH_Update(h, (void *)req, sizeof(*req)); HASH_End(h, hash, &rlen, sizeof(hash)); HASH_Destroy(h); memcpy(req->hash, hash, sizeof(req->hash)); return 0; } static int sha_verify(fence_req_t *req, void *key, size_t key_len) { unsigned char hash[SHA512_LENGTH]; unsigned char pkt_hash[SHA512_LENGTH]; HASHContext *h = NULL; HASH_HashType ht; unsigned int rlen; int ret; switch(req->hashtype) { case HASH_SHA1: ht = HASH_AlgSHA1; break; case HASH_SHA256: ht = HASH_AlgSHA256; break; case HASH_SHA512: ht = HASH_AlgSHA512; break; default: dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__); return 0; } if (!key || !key_len) { dbg_printf(3, "%s: Hashing requested when we have no key data\n", __FUNCTION__); return 0; } memset(hash, 0, sizeof(hash)); h = HASH_Create(ht); if (!h) return 0; memcpy(pkt_hash, req->hash, sizeof(pkt_hash)); memset(req->hash, 0, sizeof(req->hash)); HASH_Begin(h); HASH_Update(h, key, key_len); HASH_Update(h, (void *)req, sizeof(*req)); HASH_End(h, hash, &rlen, sizeof(hash)); HASH_Destroy(h); memcpy(req->hash, pkt_hash, sizeof(req->hash)); ret = !memcmp(hash, pkt_hash, sizeof(hash)); if (!ret) { printf("Hash mismatch:\nPKT = "); print_hash(pkt_hash, sizeof(pkt_hash)); printf("\nEXP = "); print_hash(hash, sizeof(hash)); printf("\n"); } return ret; } int sign_request(fence_req_t *req, void *key, size_t key_len) { memset(req->hash, 0, sizeof(req->hash)); switch(req->hashtype) { case HASH_NONE: dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__); return 0; case HASH_SHA1: case HASH_SHA256: case HASH_SHA512: return sha_sign(req, key, key_len); default: break; } return -1; } int verify_request(fence_req_t *req, fence_hash_t min, void *key, size_t key_len) { if (req->hashtype < min) { printf("Hash type not strong enough (%d < %d)\n", req->hashtype, min); return 0; } switch(req->hashtype) { case HASH_NONE: return 1; case HASH_SHA1: case HASH_SHA256: case HASH_SHA512: return sha_verify(req, key, key_len); default: break; } return 0; } int sha_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { fd_set rfds; struct timeval tv; unsigned char hash[MAX_HASH_LENGTH]; unsigned char challenge[MAX_HASH_LENGTH]; unsigned char response[MAX_HASH_LENGTH]; int devrand; int ret; HASHContext *h; HASH_HashType ht; unsigned int rlen; devrand = open("/dev/urandom", O_RDONLY); if (devrand < 0) { dbg_printf(1, "Error: open /dev/urandom: %s", strerror(errno)); return 0; } tv.tv_sec = timeout; tv.tv_usec = 0; ret = _read_retry(devrand, challenge, sizeof(challenge), &tv); if (ret < 0) { dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno)); close(devrand); return 0; } close(devrand); tv.tv_sec = timeout; tv.tv_usec = 0; ret = _write_retry(fd, challenge, sizeof(challenge), &tv); if (ret < 0) { dbg_printf(2, "Error: write: %s", strerror(errno)); return 0; } switch(auth) { case HASH_SHA1: ht = HASH_AlgSHA1; break; case HASH_SHA256: ht = HASH_AlgSHA256; break; case HASH_SHA512: ht = HASH_AlgSHA512; break; default: return 0; } memset(hash, 0, sizeof(hash)); h = HASH_Create(ht); if (!h) return 0; HASH_Begin(h); HASH_Update(h, key, key_len); HASH_Update(h, challenge, sizeof(challenge)); HASH_End(h, hash, &rlen, sizeof(hash)); HASH_Destroy(h); memset(response, 0, sizeof(response)); FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { dbg_printf(0, "Error: select: %s\n", strerror(errno)); return 0; } tv.tv_sec = timeout; tv.tv_usec = 0; ret = _read_retry(fd, response, sizeof(response), &tv); if (ret < 0) { dbg_printf(0, "Error reading challenge response: %s", strerror(errno)); return 0; } else if (ret < sizeof(response)) { dbg_printf(0, "read data from socket is too short(actual: %d, expected: %lu)\n", ret, sizeof(response)); return 0; } ret = !memcmp(response, hash, sizeof(response)); if (!ret) { printf("Hash mismatch:\nC = "); print_hash(challenge, sizeof(challenge)); printf("\nH = "); print_hash(hash, sizeof(hash)); printf("\nR = "); print_hash(response, sizeof(response)); printf("\n"); } return ret; } int sha_response(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { fd_set rfds; struct timeval tv; unsigned char challenge[MAX_HASH_LENGTH]; unsigned char hash[MAX_HASH_LENGTH]; HASHContext *h; HASH_HashType ht; unsigned int rlen; int ret; FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { dbg_printf(2, "Error: select: %s\n", strerror(errno)); return 0; } tv.tv_sec = timeout; tv.tv_usec = 0; if (_read_retry(fd, challenge, sizeof(challenge), &tv) < 0) { dbg_printf(2, "Error reading challenge hash: %s\n", strerror(errno)); return 0; } switch(auth) { case AUTH_SHA1: ht = HASH_AlgSHA1; break; case AUTH_SHA256: ht = HASH_AlgSHA256; break; case AUTH_SHA512: ht = HASH_AlgSHA512; break; default: dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); return 0; } memset(hash, 0, sizeof(hash)); h = HASH_Create(ht); /* */ if (!h) return 0; HASH_Begin(h); HASH_Update(h, key, key_len); HASH_Update(h, challenge, sizeof(challenge)); HASH_End(h, hash, &rlen, sizeof(hash)); HASH_Destroy(h); tv.tv_sec = timeout; tv.tv_usec = 0; ret = _write_retry(fd, hash, sizeof(hash), &tv); if (ret < 0) { perror("write"); return 0; } else if (ret < sizeof(hash)) { dbg_printf(2, "Only part of hash is written(actual: %d, expected: %lu)\n", ret, sizeof(hash)); return 0; } return 1; } int sock_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { switch(auth) { case AUTH_NONE: dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); return 1; case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: return sha_challenge(fd, auth, key, key_len, timeout); default: break; } return -1; } int sock_response(int fd, fence_auth_type_t auth, void *key, size_t key_len, int timeout) { switch(auth) { case AUTH_NONE: dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); return 1; case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: return sha_response(fd, auth, key, key_len, timeout); default: break; } return -1; } int read_key_file(char *file, char *key, size_t max_len) { int fd; int nread, remain = max_len; char *p; dbg_printf(3, "Reading in key file %s into %p (%d max size)\n", file, key, (int)max_len); fd = open(file, O_RDONLY); if (fd < 0) { dbg_printf(2, "Error opening key file: %s\n", strerror(errno)); return -1; } memset(key, 0, max_len); p = key; remain = max_len; while (remain) { nread = read(fd, p, remain); if (nread < 0) { if (errno == EINTR) continue; dbg_printf(2, "Error from read: %s\n", strerror(errno)); close(fd); return -1; } if (nread == 0) { dbg_printf(3, "Stopped reading @ %d bytes\n", (int)max_len-remain); break; } p += nread; remain -= nread; } close(fd); dbg_printf(3, "Actual key length = %d bytes\n", (int)max_len-remain); return (int)(max_len - remain); } fence-virt-1.0.0/common/tcp.c000066400000000000000000000202041356743026700160070ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2002-2004, 2006 Copyright Mission Critical Linux, 2000 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file * * @author Lon H. Hohberger * @author Jeff Moyer */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout); static int get_addr(const char *hostname, int family, struct sockaddr_storage *addr); /** Set close-on-exec bit option for a socket. @param fd Socket to set CLOEXEC flag @return 0 on success, -1 on failure @see fcntl */ static int set_cloexec(int fd) { int flags = fcntl(fd, F_GETFD, 0); flags |= FD_CLOEXEC; return fcntl(fd, F_SETFD, flags); } /** Bind to a port on the local IPv6 stack @param addr_str Address to listen on, NULL for inaddr6_any @param port Port to bind to @param backlog same as backlog for listen(2) @return 0 on success, -1 on failure @see ipv4_bind */ int ipv6_listen(const char *addr_str, uint16_t port, int backlog) { struct sockaddr_in6 _sin6; int fd, ret; dbg_printf(4, "%s: Setting up ipv6 listen socket for %s:%d\n", __FUNCTION__, addr_str, port); memset(&_sin6, 0, sizeof(_sin6)); _sin6.sin6_family = PF_INET6; _sin6.sin6_port = htons(port); _sin6.sin6_flowinfo = 0; if (addr_str == NULL) { _sin6.sin6_addr = in6addr_any; } else { struct sockaddr_storage ss; if (get_addr(addr_str, AF_INET6, &ss) == -1) { dbg_printf(4, "%s: Can't get addr for %s\n", __FUNCTION__, addr_str); return -1; } memcpy(&_sin6.sin6_addr, &((struct sockaddr_in6 *)&ss)->sin6_addr, sizeof(_sin6.sin6_addr)); } fd = socket(PF_INET6, SOCK_STREAM, 0); if (fd < 0) return -1; ret = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret)); ret = set_cloexec(fd); if (ret < 0) { close(fd); return -1; } ret = bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6)); if (ret < 0) { close(fd); return -1; } if (listen(fd, backlog) < 0){ close(fd); return -1; } dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); return fd; } /** Bind to a port on the local IPv4 stack @param addr_str Address to listen on, NULL for inaddr_any @param port Port to bind to @param backlog same as backlog for listen(2) @return 0 on success, -1 on failure @see ipv6_bind */ int ipv4_listen(const char *addr_str, uint16_t port, int backlog) { struct sockaddr_in _sin; int fd, ret; dbg_printf(4, "%s: Setting up ipv4 listen socket for %s:%d\n", __FUNCTION__, addr_str, port); _sin.sin_family = PF_INET; _sin.sin_port = htons(port); if (addr_str == NULL) { _sin.sin_addr.s_addr = htonl(INADDR_ANY); } else { struct sockaddr_storage ss; if (get_addr(addr_str, AF_INET, &ss) == -1) { dbg_printf(4, "%s: Can't get addr for %s\n", __FUNCTION__, addr_str); return -1; } memcpy(&_sin.sin_addr, &((struct sockaddr_in *)&ss)->sin_addr, sizeof(_sin.sin_addr)); } fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) return -1; ret = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret)); ret = set_cloexec(fd); if (ret < 0) { close(fd); return -1; } ret = bind(fd, (struct sockaddr *)&_sin, sizeof(_sin)); if (ret < 0) { close(fd); return -1; } if (listen(fd, backlog) < 0){ close(fd); return -1; } dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); return fd; } /** Connect via ipv6 socket to a given IP address and port. @param in6_addr IPv6 address to connect to @param port Port to connect to @param timeout Timeout, in seconds, to wait for a completed connection @return 0 on success, -1 on failure @see connect_nb, ipv4_connect */ int ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout) { struct sockaddr_in6 _sin6; int fd, ret; dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__); fd = socket(PF_INET6, SOCK_STREAM, 0); if (fd < 0) return -1; memset(&_sin6, 0, sizeof(_sin6)); _sin6.sin6_family = PF_INET6; _sin6.sin6_port = htons(port); _sin6.sin6_flowinfo = 0; memcpy(&_sin6.sin6_addr, in6_addr, sizeof(_sin6.sin6_addr)); ret = connect_nb(fd, (struct sockaddr *)&_sin6, sizeof(_sin6), timeout); if (ret < 0) { close(fd); return -1; } dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); return fd; } /** Connect via ipv4 socket to a given IP address and port. @param in_addr IPv4 address to connect to @param port Port to connect to @param timeout Timeout, in seconds, to wait for a completed connection @return 0 on success, -1 on failure @see connect_nb, ipv6_connect */ int ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout) { struct sockaddr_in _sin; int fd, ret; dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__); fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) return -1; _sin.sin_family = PF_INET; _sin.sin_port = htons(port); memcpy(&_sin.sin_addr, in_addr, sizeof(_sin.sin_addr)); ret = connect_nb(fd, (struct sockaddr *)&_sin, sizeof(_sin), timeout); if (ret < 0) { close(fd); return -1; } dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); return fd; } /** Connect in a non-blocking fashion to the designated address. @param fd File descriptor to connect @param dest sockaddr (ipv4 or ipv6) to connect to. @param len Length of dest @param timeout Timeout, in seconds, to wait for a completed connection. @return 0 on success, -1 on failure. */ static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout) { int ret, flags = 1, err; unsigned l; fd_set rfds, wfds; struct timeval tv; /* * Use TCP Keepalive */ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags))<0) return -1; /* Set up non-blocking connect */ flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); ret = connect(fd, dest, len); if ((ret < 0) && (errno != EINPROGRESS)) return -1; if (ret != 0) { FD_ZERO(&rfds); FD_SET(fd, &rfds); FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = timeout; tv.tv_usec = 0; if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) { errno = ETIMEDOUT; return -1; } /* XXX check for -1 from select */ if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) { l = sizeof(err); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &l) < 0) { close(fd); return -1; } if (err != 0) { close(fd); errno = err; return -1; } fcntl(fd, F_SETFL, flags); return 0; } } errno = EIO; return -1; } static int get_addr(const char *hostname, int family, struct sockaddr_storage *addr) { struct addrinfo *res; size_t len; struct addrinfo ai; memset(&ai, 0, sizeof(ai)); ai.ai_family = family; if (getaddrinfo(hostname, NULL, &ai, &res) != 0) return -1; switch (res->ai_addr->sa_family) { case AF_INET: len = sizeof(struct sockaddr_in); break; case AF_INET6: len = sizeof(struct sockaddr_in6); break; default: goto out_fail; } if (len < (size_t) res->ai_addrlen) goto out_fail; memcpy(addr, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return 0; out_fail: freeaddrinfo(res); return -1; } fence-virt-1.0.0/config/000077500000000000000000000000001356743026700150345ustar00rootroot00000000000000fence-virt-1.0.0/config/Makefile.am000066400000000000000000000021341356743026700170700ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = config.l config.y fence_virt.conf noinst_LIBRARIES = libsimpleconfig.a libsimpleconfig_a_SOURCES = \ simpleconfig.c nodist_libsimpleconfig_a_SOURCES = \ y.tab.c \ config.c libsimpleconfig_a_CFLAGS = $(AM_CFLAGS) noinst_HEADERS = config-stack.h sysconf_DATA = fence_virt.conf # local rules y.tab.c: config.y $(YACC) -d $^ config.c: y.tab.c config.l $(LEX) -oconfig.c $(srcdir)/config.l clean-local: rm -f config.tab.c config.tab.h config.c y.tab.c y.tab.h fence-virt-1.0.0/config/config-stack.h000066400000000000000000000012151356743026700175540ustar00rootroot00000000000000#ifndef _CONFIG_STACK_H #define _CONFIG_STACK_H int yyparse (void); extern FILE *yyin; struct value { char *id; char *val; struct value *next; }; struct node { char *id; char *val; struct node *nodes; struct value *values; struct node *next; }; struct parser_context { struct value *val_list; struct node *node_list; struct parser_context *next; }; extern struct value *val_list; extern struct node *node_list; extern struct parser_context *context_stack; int _sc_value_add(char *id, char *val, struct value **list); int _sc_node_add(char *id, char *val, struct value *vallist, struct node *nodelist, struct node **list); #endif fence-virt-1.0.0/config/config.l000066400000000000000000000030211356743026700164520ustar00rootroot00000000000000%{ #include "config.h" #include #include #include #include #include "config-stack.h" #include "y.tab.h" #include "simpleconfig.h" /* Some distributions can't use the output from flex without help */ #define ECHO if(fwrite( yytext, yyleng, 1, yyout )) struct value *val_list = NULL; struct node *node_list = NULL; struct parser_context *context_stack = NULL; int _line_count = 1; %} %% [\n] { ++_line_count; } [ \t]* {} \#[^\n]* {} "{" { struct parser_context *c = NULL; //printf("obrace\n"); c = malloc(sizeof(*c)); assert(c); c->next = context_stack; c->val_list = val_list; c->node_list = node_list; context_stack = c; val_list = NULL; node_list = NULL; return T_OBRACE; } "}" { return T_CBRACE; } ";" { return T_SEMI; } "=" { return T_EQ; } [^ \t{};=\"\n]+ { yylval.sval = strdup(yytext); return T_ID; } \"[^\"]+\" { yylval.sval = strdup(yytext+1); yylval.sval[strlen(yytext)-2] = 0; return T_VAL; } %% void reset_vars(void) { _line_count = 1; } int yywrap(void) { return 1; } #ifdef STANDALONE int main(int argc, char *argv[]) { char value[80]; config_object_t *c = NULL; yyout = fopen("/dev/null","w"); c = sc_init(); sc_parse(c, NULL); sc_dump(c, stdout); if (argc == 2) { if (sc_get(c, argv[1], value, sizeof(value)) == 0) printf("%s = %s\n", argv[1], value); else printf("Not found\n"); } else if (argc == 3) { printf("---------\n"); if (sc_set(c, argv[1], argv[2]) == 0) sc_dump(c, stdout); } sc_release(c); return 0; } #endif fence-virt-1.0.0/config/config.y000066400000000000000000000046611356743026700165020ustar00rootroot00000000000000%{ #include "config.h" #include #include #include #include #include "config-stack.h" extern int yylex (void); int yyerror(const char *foo); int _sc_value_add(char *id, char *val, struct value **list) { struct value *v; v = malloc(sizeof(*v)); assert(v); memset(v, 0, sizeof(*v)); v->id = id; v->val = val; //snprintf(v->id, sizeof(v->id), "%s", id); //snprintf(v->val, sizeof(v->val), "%s", val); //printf("add %s %s on to %p\n", id, val, *list); v->next = *list; *list = v; //printf("new list %p\n", *list); return 0; } int _sc_node_add(char *id, char *val, struct value *vallist, struct node *nodelist, struct node **list) { struct node *n; n = malloc(sizeof(*n)); assert(n); //printf("nodes %p values %p\n", nodelist, vallist); memset(n, 0, sizeof(*n)); //snprintf(n->id, sizeof(n->id), "%s", id); n->id = id; /* malloc'd during parsing */ n->val = val; /* malloc'd during parsing */ n->values = vallist; n->nodes = nodelist; n->next = *list; *list = n; return 0; } %} %token T_ID %token T_VAL %token T_OBRACE T_CBRACE T_EQ T_SEMI %start stuff %union { char *sval; int ival; } %% node: T_ID T_OBRACE stuff T_CBRACE { struct parser_context *c = NULL; c = context_stack; _sc_node_add($1, NULL, val_list, node_list, &c->node_list); val_list = c->val_list; node_list = c->node_list; context_stack = c->next; free(c); } | T_ID T_EQ T_VAL T_OBRACE stuff T_CBRACE { struct parser_context *c = NULL; c = context_stack; _sc_node_add($1, $3, val_list, node_list, &c->node_list); val_list = c->val_list; node_list = c->node_list; context_stack = c->next; free(c); } | T_ID T_OBRACE T_CBRACE { struct parser_context *c = NULL; c = context_stack; _sc_node_add($1, NULL, val_list, node_list, &c->node_list); val_list = c->val_list; node_list = c->node_list; context_stack = c->next; free(c); } | T_ID T_EQ T_VAL T_OBRACE T_CBRACE { struct parser_context *c = NULL; c = context_stack; _sc_node_add($1, $3, val_list, node_list, &c->node_list); val_list = c->val_list; node_list = c->node_list; context_stack = c->next; free(c); } ; stuff: node stuff | assign stuff | node | assign ; assign: T_ID T_EQ T_VAL T_SEMI { _sc_value_add($1, $3, &val_list); } ; %% extern int _line_count; int yyerror(const char *foo) { printf("%s on line %d\n", foo, _line_count); return 0; } fence-virt-1.0.0/config/fence_virt.conf000066400000000000000000000004201356743026700200230ustar00rootroot00000000000000fence_virtd { listener = "multicast"; backend = "libvirt"; } listeners { multicast { key_file = "/etc/cluster/fence_xvm.key"; address = "225.0.0.12"; # Needed on Fedora systems interface = "virbr0"; } } backends { libvirt { uri = "qemu:///system"; } } fence-virt-1.0.0/config/simpleconfig.c000066400000000000000000000164761356743026700176750ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include "simpleconfig.h" #include "config-stack.h" #include "static_map.h" static pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER; static int print_value(struct value *v, int depth, FILE *fp) { int x; if (v->val == NULL) return 0; for (x = 0; x < depth; x++) fprintf(fp, "\t"); fprintf(fp, "%s = \"%s\";\n", v->id, v->val); return 0; } static void _sc_dump_d(struct node *node, int depth, FILE *fp) { struct node *n; struct value *v; int x; if (!node) { //printf("Empty node\n"); return; } for (x = 0; x < depth; x++) fprintf(fp, "\t"); if (node->val) { fprintf(fp, "%s = \"%s\" {\n", node->id, node->val); } else { fprintf(fp, "%s {\n", node->id); } for (n = node->nodes; n; n = n->next) { _sc_dump_d(n, depth+1, fp); } for (v = node->values; v; v = v->next) { print_value(v, depth+1, fp); } for (x = 0; x < depth; x++) fprintf(fp, "\t"); fprintf(fp, "}\n\n"); } static void _sc_dump(void *config, FILE *fp) { struct node *n, *node; struct value *v, *values; if (!config) return; values = ((struct parser_context *)config)->val_list; node = ((struct parser_context *)config)->node_list; for (n = node; n; n = n->next) { _sc_dump_d(n, 0, fp); } for (v = values; v; v = v->next) { print_value(v, 0, fp); } } static int free_value(struct value *v) { int x; if (v) { free(v->id); free(v->val); free(v); } return 0; } static void _sc_free_node(struct node *node) { struct node *n; struct value *v; int x; if (!node) return; while (node->nodes) { n = node->nodes; if (n) { node->nodes = node->nodes->next; _sc_free_node(n); } } while (node->values) { v = node->values; node->values = node->values->next; free_value(v); } free(node->id); free(node); } static int _sc_free(void *config) { struct node *n, *nlist; struct value *v, *vlist; if (!config) return -1; vlist = ((struct parser_context *)config)->val_list; nlist = ((struct parser_context *)config)->node_list; while (nlist) { n = nlist; nlist = nlist->next; _sc_free_node(n); } ((struct parser_context *)config)->node_list = NULL; while (vlist) { v = vlist; vlist = vlist->next; free_value(v); } ((struct parser_context *)config)->val_list = NULL; free(config); return 0; } static int _sc_get(void *config, const char *key, char *value, size_t valuesz) { char buf[1024]; struct node *n, *node; struct value *v, *values; char *ptr; char *slash; char *bracket; char *id; int req_index = 0; int curr_index = 0; int found; if (!config) return -1; node = ((struct parser_context *)config)->node_list; values = ((struct parser_context *)config)->val_list; assert(strlen(key) < sizeof(buf)); ptr = (char *)key; top: while ((slash = strchr(ptr, '/'))) { memset(buf, 0, sizeof(buf)); strncpy(buf, ptr, (slash - ptr)); ptr = ++slash; id = NULL; bracket = strchr(buf, '['); if (bracket) { *bracket = 0; ++bracket; id = bracket; bracket = strchr(bracket, ']'); if (!bracket) return 1; *bracket = 0; if (id[0] == '@') { ++id; if (!strlen(id)) { return 1; } } else { req_index = atoi(id); if (req_index <= 0) return 1; id = NULL; } } found = 0; curr_index = 0; for (n = node; n; n = n->next) { if (strcasecmp(n->id, buf)) continue; ++curr_index; if (req_index && (curr_index != req_index)) { continue; } else if (id && strcasecmp(n->val, id)) { continue; } node = n->nodes; values = n->values; found = 1; break; } if (!found) return 1; } if (ptr[0] != '@') { strncpy(buf, ptr, sizeof(buf) - 1); id = NULL; bracket = strchr(buf, '['); if (bracket) { *bracket = 0; ++bracket; id = bracket; bracket = strchr(bracket, ']'); if (!bracket) return 1; *bracket = 0; if (id[0] == '@') { ++id; if (!strlen(id)) { return 1; } } else { req_index = atoi(id); if (req_index <= 0) return 1; id = NULL; } } found = 0; curr_index = 0; for (n = node; n; n = n->next) { if (strcasecmp(n->id, buf)) continue; ++curr_index; if (req_index && (curr_index != req_index)) { continue; } else if (id && strcasecmp(n->val, id)) { continue; } if (node->val) { strncpy(value, node->val, valuesz); return 0; } return 1; } } ++ptr; found = 0; id = NULL; strncpy(buf, ptr, sizeof(buf) - 1); bracket = strchr(buf, '['); req_index = 0; curr_index = 0; if (bracket) { *bracket = 0; ++bracket; id = bracket; bracket = strchr(bracket, ']'); if (!bracket) return 1; *bracket = 0; req_index = atoi(id); if (req_index <= 0) return 1; id = NULL; } for (v = values; v; v = v->next) { if (strcasecmp(v->id, buf)) continue; ++curr_index; if (req_index && (curr_index != req_index)) continue; snprintf(value, valuesz, "%s", v->val); return 0; } return 1; } static int _sc_set(void *config, const char *key, const char *value) { char buf[1024]; struct node *n, **nodes = &((struct parser_context *)config)->node_list; struct value *v, **values = &((struct parser_context *)config)->val_list; char *ptr; char *slash; char *id_dup, *val_dup; int found = 0; ptr = (char *)key; while ((slash = strchr(ptr, '/'))) { memset(buf, 0, sizeof(buf)); strncpy(buf, ptr, (slash - ptr)); ptr = ++slash; found = 0; for (n = *nodes; n; n = n->next) { if (strcasecmp(n->id, buf)) continue; nodes = &n->nodes; values = &n->values; found = 1; break; } if (!found) { id_dup = strdup(buf); if (!id_dup) return -1; _sc_node_add(id_dup, NULL, NULL, NULL, nodes); n = *nodes; nodes = &n->nodes; values = &n->values; } } if (ptr[0] != '@') return 1; ++ptr; for (v = *values; v; v = v->next) { if (strcasecmp(v->id, ptr)) continue; ptr = v->val; if (value) { v->val = strdup(value); if (!v->val) { v->val = ptr; return -1; } } else { v->val = NULL; } free(ptr); return 0; } if (!value) return 0; id_dup = strdup(ptr); if (!id_dup) return -1; val_dup = strdup(value); if (!val_dup) { free(id_dup); return -1; } _sc_value_add(id_dup, val_dup, values); return 0; } static int _sc_parse(const char *filename, void **config) { struct parser_context *c; FILE *fp = NULL; int ret = 0; if (!config) return -1; pthread_mutex_lock(&parser_mutex); if (filename) { fp = fopen(filename, "r"); yyin = fp; if (fp) ret = yyparse(); else ret = 1; } else { ret = 1; } c = malloc(sizeof(*c)); if (!c) { ret = -1; goto out_unlock; } c->node_list = node_list; c->val_list = val_list; c->next = NULL; val_list = NULL; node_list = NULL; *config = (void *)c; if (fp) fclose(fp); out_unlock: pthread_mutex_unlock(&parser_mutex); return ret; } static const config_object_t sc_object = { .get = _sc_get, .set = _sc_set, .parse = _sc_parse, .free = _sc_free, .dump = _sc_dump, .info = NULL }; config_object_t * sc_init(void) { config_object_t *o; o = malloc(sizeof(*o)); if (!o) return NULL; memset(o, 0, sizeof(*o)); memcpy(o, &sc_object, sizeof(*o)); return o; } void sc_release(config_object_t *c) { sc_free(c); free(c); } fence-virt-1.0.0/configure.ac000066400000000000000000000237301356743026700160620ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) AC_INIT([fence-virt], [m4_esyscmd([build-aux/git-version-gen .tarball-version .gitarchivever])], [developers@clusterlabs.org]) # Don't let AC_PROC_CC (invoked by AC_USE_SYSTEM_EXTENSIONS) replace # undefined CFLAGS with -g -O2, overriding our special OPT_CFLAGS. : ${CFLAGS=""} AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([1.13 dist-bzip2 dist-xz color-tests -Wno-portability subdir-objects]) # sanitize paths if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$systemddir" = "NONE/lib/systemd/system"; then systemddir=/lib/systemd/system fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi LT_PREREQ([2.2.6]) # --enable-new-dtags: Use RUNPATH instead of RPATH. # It is necessary to have this done before libtool does linker detection. # See also: https://github.com/kronosnet/kronosnet/issues/107 # --as-needed: Modern systems have builtin ceil() making -lm superfluous but # AC_SEARCH_LIBS can't detect this because it tests with a false prototype AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags], [AM_LDFLAGS=-Wl,--enable-new-dtags], [AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])]) AX_CHECK_LINK_FLAG([-Wl,--as-needed], [AM_LDFLAGS="$AM_LDFLAGS -Wl,--as-needed"]) AM_LDFLAGS="$AM_LDFLAGS -fPIC -fPIE -Wl,-z,now" AC_SUBST([AM_LDFLAGS]) saved_LDFLAGS="$LDFLAGS" LDFLAGS="$AM_LDFLAGS $LDFLAGS" LT_INIT LDFLAGS="$saved_LDFLAGS" AM_CFLAGS="-fPIC -fPIE -I\$(top_srcdir)/include -Wall -Wformat=2 -Wstrict-prototypes -Wshadow -Wno-unused -D_GNU_SOURCE" AC_SUBST([AM_CFLAGS]) COMMON_LDFLAGS="-Wl,-wrap,syslog,-wrap,closelog" AC_SUBST([COMMON_LDFLAGS]) COMMON_LIBS="-Wl,-Bstatic -L\$(top_builddir)/common -lfence_virt -Wl,-Bdynamic" AC_SUBST([COMMON_LIBS]) CONFIG_LIBS="-L\$(top_builddir)/config -lsimpleconfig" AC_SUBST([CONFIG_LIBS]) sysconf=$(eval echo $sysconfdir) AC_DEFINE_UNQUOTED([SYSCONFDIR], ["$sysconf"], [Default config dir]) ### AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([server/main.c]) AC_CONFIG_HEADER([include/config.h]) AC_CANONICAL_HOST AC_LANG([C]) # Checks for programs. AC_PROG_YACC AC_PROG_CC AC_PROG_CXX AC_PROG_LEX AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET PKG_PROG_PKG_CONFIG # Checks for libraries. AX_PTHREAD(,[AC_MSG_ERROR([POSIX threads support is required])]) PKG_CHECK_MODULES([nss], [nss]) PKG_CHECK_MODULES([xml2], [libxml-2.0]) PKG_CHECK_MODULES([uuid], [uuid]) saved_LIBS="$LIBS" LIBS= AC_SEARCH_LIBS([dlopen], [dl dld], , [AC_MSG_ERROR([dlopen not found])]) AC_SUBST([dl_LIBS], [$LIBS]) LIBS="$saved_LIBS" # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_HEADER_TIME # Checks for library functions. AC_FUNC_CLOSEDIR_VOID AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL AC_FUNC_STAT AC_CHECK_FUNCS([gettimeofday memmove memset select socket strcasecmp strchr strdup strerror]) # # Modular build for fence_virtd to split up dependencies # (default) # AC_ARG_ENABLE(modules, [AS_HELP_STRING([--disable-modules], [Disable modular build])], [ modules=$enableval ], [ modules=yes ]) AM_CONDITIONAL([modularbuild], [test "x$modules" == "xyes"]) if test "x$modules" == "xyes"; then AC_DEFINE_UNQUOTED([_MODULE], [1], [modular build]) fi ### The following options only are used when $modules="yes" ### # Null plugin: Disabled by default AC_ARG_ENABLE(null-plugin, [AS_HELP_STRING([--enable-null-plugin], [Enable null (no-op) backend plugin])], [ modnull=$enableval ], [ modnull=no ]) AM_CONDITIONAL([modnull], [test "x$modnull" == "xyes"]) # libvirt plugin: Enabled by default AC_ARG_ENABLE(libvirt-plugin, [AS_HELP_STRING([--disable-libvirt-plugin], [Disable local-mode libvirt backend plugin])], [ modlibvirt=$enableval ], [ modlibvirt=yes ]) AM_CONDITIONAL([modlibvirt], [test "x$modlibvirt" == "xyes"]) if test "x$modlibvirt" == "xyes"; then PKG_CHECK_MODULES([virt], [libvirt]) fi # cpg plugin: Disabled by default AC_ARG_ENABLE(cpg-plugin, [AS_HELP_STRING([--enable-cpg-plugin], [Enable CPG/libvirt backend plugin])], [ modcpg=$enableval ], [ modcpg=no ]) AM_CONDITIONAL([modcpg], [test "x$modcpg" == "xyes"]) if test "x$modcpg" == "xyes"; then PKG_CHECK_MODULES([cpg], [libcpg]) fi # (broken!) libvirt-qmf plugin: Disabled by default AC_ARG_ENABLE(libvirt-qmf-plugin, [AS_HELP_STRING([--enable-libvirt-qmf-plugin], [Enable libvirt-qmf backend plugin])], [ modlibvirtqmf=$enableval ], [ modlibvirtqmf=no ]) AM_CONDITIONAL([modlibvirtqmf], [test "x$modlibvirtqmf" == "xyes"]) if test "x$modlibvirtqmf" == "xyes"; then PKG_CHECK_MODULES([qpid], [qpid]) fi # (broken with pcmk 2.0!) pm-fence plugin: Disabled by default AC_ARG_ENABLE(pm-fence-plugin, [AS_HELP_STRING([--enable-pm-fence-plugin], [Enable pm-fence backend plugin])], [ modpmfence=$enableval ], [ modpmfence=no ]) AM_CONDITIONAL([modpmfence], [test "x$modpmfence" == "xyes"]) if test "x$modpmfence" == "xyes"; then PKG_CHECK_MODULES([cib], [pacemaker-cib]) PKG_CHECK_MODULES([ncurses], [ncurses]) PKG_CHECK_MODULES([glib2], [glib-2.0]) AC_SEARCH_LIBS([read_attr_delegate], [cib], [ ], [ AC_DEFINE_UNQUOTED([PM_1_0], [1], [pacemaker 1.0]) ]) fi # multicast plugin: Enabled by default AC_ARG_ENABLE(multicast-plugin, [AS_HELP_STRING([--disable-multicast-plugin], [Disable multicast listener plugin])], [ modmulticast=$enableval ], [ modmulticast=yes ]) AM_CONDITIONAL([modmulticast], [test "x$modmulticast" == "xyes"]) # tcp plugin: Enabled by default AC_ARG_ENABLE(tcp-plugin, [AS_HELP_STRING([--disable-tcp-plugin], [Disable TCP listener plugin])], [ modtcp=$enableval ], [ modtcp=yes ]) AM_CONDITIONAL([modtcp], [test "x$modtcp" == "xyes"]) # serial/libvirt plugin: Enabled by default AC_ARG_ENABLE(serial-plugin, [AS_HELP_STRING([--disable-serial-plugin], [Disable serial listener plugin])], [ modserial=$enableval ], [ modserial=yes ]) AM_CONDITIONAL([modserial], [test "x$modserial" == "xyes"]) # vsock plugin: Enabled by default AC_ARG_ENABLE(vsock-plugin, [AS_HELP_STRING([--disable-vsock-plugin], [Disable TCP listener plugin])], [ modvsock=$enableval ], [ modvsock=yes ]) AM_CONDITIONAL([modvsock], [test "x$modvsock" == "xyes"]) # # Compatibility symlink: enabled by default # AC_ARG_ENABLE(xvm-compat, [AS_HELP_STRING([--disable-xvm-compat], [Disable fence_xvm symlink compatibility])], [ xvmcompat=$enableval ], [ xvmcompat=yes ]) AM_CONDITIONAL([xvmcompat], [test "x$xvmcompat" == "xyes"]) AX_PROG_DATE AS_IF([test "$ax_cv_prog_date_gnu_date:$ax_cv_prog_date_gnu_utc" = yes:yes], [UTC_DATE_AT="date -u -d@"], [AS_IF([test "x$ax_cv_prog_date_bsd_date" = xyes], [UTC_DATE_AT="date -u -r"], [AC_MSG_ERROR([date utility unable to convert epoch to UTC])])]) AC_SUBST([UTC_DATE_AT]) # Try to detect the appropriate conf dir. Several systems have both /etc/default # and /etc/sysconfig but latter is always primary. AC_ARG_VAR(initconfdir, [directory for initscripts configuration]) if test "x$initconfdir" = x; then AC_CHECK_FILE(/etc/conf.d, [initconfdir='$(sysconfdir)/conf.d}'], [# Gentoo/Arch AC_CHECK_FILE(/etc/sysconfig, [initconfdir='$(sysconfdir)/sysconfig'], [# RedHat/Fedora/Slax/Mandriva/S AC_CHECK_FILE(/etc/default, [initconfdir='$(sysconfdir)/default'], [# Debian/Ubuntu AC_MSG_ERROR([could not determine system initscripts config dir; please set initconfdir manually.])])])]) fi AC_ARG_VAR([SOURCE_EPOCH],[last modification date of the source]) AC_MSG_NOTICE([trying to determine source epoch]) AC_MSG_CHECKING([for source epoch in \$SOURCE_EPOCH]) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in source_epoch file]) AS_IF([test -e "$srcdir/source_epoch"], [read SOURCE_EPOCH <"$srcdir/source_epoch" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch baked in by gitattributes export-subst]) SOURCE_EPOCH='$Format:%at$' # template for rewriting by git-archive AS_CASE([$SOURCE_EPOCH], [?Format:*], # was not rewritten [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in \$SOURCE_DATE_EPOCH]) AS_IF([test "x$SOURCE_DATE_EPOCH" != x], [SOURCE_EPOCH="$SOURCE_DATE_EPOCH" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([whether git log can provide a source epoch]) SOURCE_EPOCH=f${SOURCE_EPOCH#\$F} # convert into git log --pretty format SOURCE_EPOCH=$(cd "$srcdir" && git log -1 --pretty=${SOURCE_EPOCH%$} 2>/dev/null) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no, using current time and breaking reproducibility]) SOURCE_EPOCH=$(date +%s)])])], [AC_MSG_RESULT([yes])] )]) ]) AC_MSG_NOTICE([using source epoch $($UTC_DATE_AT$SOURCE_EPOCH +'%F %T %Z')]) AC_CONFIG_FILES([Makefile fence_virtd.init man/Makefile client/Makefile common/Makefile config/Makefile server/Makefile]) if test "x$VERSION" = "xUNKNOWN"; then AC_MSG_ERROR([m4_text_wrap([ configure was unable to determine the source tree's current version. This generally happens when using git archive (or the github download button) generated tarball/zip file. In order to workaround this issue, either use git clone https://github.com/ClusterLabs/fence-virt.git or use an official release tarball. Alternatively you can add a compatible version in a .tarball-version file at the top of the source tree, wipe your autom4te.cache dir and generated configure, and rerun autogen.sh. ], [ ], [ ], [76])]) fi AC_OUTPUT fence-virt-1.0.0/fence-virt.spec.in000066400000000000000000000131141356743026700171120ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2019 Red Hat, Inc. All rights reserved. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### %global alphatag @alphatag@ %global numcomm @numcomm@ %global dirty @dirty@ Name: fence-virt Summary: A pluggable fencing framework for virtual machines Version: @version@ Release: 1%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist} License: GPLv2+ URL: https://github.com/ClusterLabs/fence-virt Source0: %{name}-%{version}%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}.tar.bz2 %if 0%{?suse_version} %define nss_devel mozilla-nss-devel %define nspr_devel mozilla-nspr-devel %define systemd_units systemd %else %define nss_devel nss-devel %define nspr_devel nspr-devel %define systemd_units systemd-units %endif BuildRequires: gcc BuildRequires: corosynclib-devel libvirt-devel BuildRequires: automake autoconf libtool libxml2-devel %{nss_devel} %{nspr_devel} BuildRequires: flex bison libuuid-devel BuildRequires: %{systemd_units} Requires(post): systemd-sysv %{systemd_units} Requires(preun): %{systemd_units} Requires(postun): %{systemd_units} Conflicts: fence-agents < 3.0.5-2 %prep %setup -q -n %{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} %build ./autogen.sh %{configure} --disable-libvirt-qmf-plugin --enable-cpg-plugin make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} # Systemd unit file mkdir -p %{buildroot}/%{_unitdir}/ install -m 0644 fence_virtd.service %{buildroot}/%{_unitdir}/ rm -f %{buildroot}/%{_libdir}/%{name}/*.*a %post ccs_update_schema > /dev/null 2>&1 ||: # https://fedoraproject.org/wiki/Packaging:ScriptletSnippets#Systemd if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi %preun # https://fedoraproject.org/wiki/Packaging:ScriptletSnippets#Systemd if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable fence_virtd.service &> /dev/null || : /bin/systemctl stop fence_virtd.service &> /dev/null || : fi %postun # https://fedoraproject.org/wiki/Packaging:ScriptletSnippets#Systemd /bin/systemctl daemon-reload &> /dev/null || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart fence_virtd.service &> /dev/null || : fi %triggerun -- fence_virtd < 0.3.0-1 # https://fedoraproject.org/wiki/Packaging:ScriptletSnippets#Packages_migrating_to_a_systemd_unit_file_from_a_SysV_initscript /usr/bin/systemd-sysv-convert --save fence_virtd &> /dev/null || : /sbin/chkconfig --del fence_virtd &> /dev/null || : /bin/systemctl daemon-reload >/dev/null 2>&1 || : /bin/systemctl try-restart fence_virtd.service &> /dev/null || : %description Fencing agent for virtual machines. %files %doc COPYING TODO README %{_sbindir}/fence_virt %{_sbindir}/fence_xvm %{_mandir}/man8/fence_virt.* %{_mandir}/man8/fence_xvm.* %package -n fence-virtd Summary: Daemon which handles requests from fence-virt %description -n fence-virtd This package provides the host server framework, fence_virtd, for fence_virt. The fence_virtd host daemon is resposible for processing fencing requests from virtual machines and routing the requests to the appropriate physical machine for action. %files -n fence-virtd %{_sbindir}/fence_virtd %{_unitdir}/fence_virtd.service %config(noreplace) %{_sysconfdir}/fence_virt.conf %dir %{_libdir}/%{name} %{_libdir}/%{name}/vsock.so %{_mandir}/man5/fence_virt.conf.* %{_mandir}/man8/fence_virtd.* %package -n fence-virtd-multicast Summary: Multicast listener for fence-virtd Requires: fence-virtd %description -n fence-virtd-multicast Provides multicast listener capability for fence-virtd. %files -n fence-virtd-multicast %{_libdir}/%{name}/multicast.so %package -n fence-virtd-serial Summary: Serial VMChannel listener for fence-virtd Requires: libvirt >= 0.6.2 Requires: fence-virtd %description -n fence-virtd-serial Provides serial VMChannel listener capability for fence-virtd. %files -n fence-virtd-serial %{_libdir}/%{name}/serial.so %package -n fence-virtd-tcp Summary: TCP listener for fence-virtd Requires: fence-virtd %description -n fence-virtd-tcp Provides TCP listener capability for fence-virtd. %files -n fence-virtd-tcp %{_libdir}/%{name}/tcp.so %package -n fence-virtd-libvirt Summary: Libvirt backend for fence-virtd Requires: libvirt >= 0.6.0 Requires: fence-virtd %description -n fence-virtd-libvirt Provides fence_virtd with a connection to libvirt to fence virtual machines. Useful for running a cluster of virtual machines on a desktop. %files -n fence-virtd-libvirt %{_libdir}/%{name}/virt.so %package -n fence-virtd-cpg Summary: CPG/libvirt backend for fence-virtd Requires: corosynclib Requires: fence-virtd %description -n fence-virtd-cpg Provides fence_virtd with a connection to libvirt to fence virtual machines. Uses corosync CPG to keep track of VM locations to allow for non-local VMs to be fenced when VMs are located on corosync cluster nodes. %files -n fence-virtd-cpg %{_libdir}/%{name}/cpg.so %changelog * @date@ Autotools generated version - @version@-@specver@-@numcomm@.@alphatag@.@dirty@ - Autotools generated version fence-virt-1.0.0/fence_virt.txt000066400000000000000000000133051356743026700164560ustar00rootroot00000000000000We need a fencing agent which can work in a variety of guest cluster configurations and host configurations. Requirements 1. Nonrequirement of guest to host networking. Virtual machines may be configured to run using a nework unknown to the host operating system. Therefore, the ability to run without network communication between the guest and the hsot is required. 2. Ease of configuration. The absolute minimum possible configuration must be available. 3. Nonrequirement of host clustering software. Multiple layers of configuration sucks. While I fundamentally disagree with the general idea that running CMAN on the host constitutes a "heavyweight cluster", perception is important. 4. Ability to support RHEV-M, oVirt server, and other virtual machine management technologies. This is beneficial from a security standpoint since it is assumed the management server will be aware of what VMs are allowed to fence what other VMs. 5. Upgrade compatibility with fence_xvm from a configuration standpoint. This may be provided by a symlink over fence_xvm. If this feature can not be provided as a matter of design, a method to convert an existing fence_xvm/fence_xvmd configuration to fence_virt must be present. Guest to Host Interaction ------------------------- The proposal is to use various communications media plugins in order to facilitate flexibility with respect to how virtual machine environments are configured. There are at least 3 simple plugins for guest/client to host/server communications: * Direct serial. The guest sends fencing requests out via /dev/ttySX in the guest. The host is listening on a Unix domain socket[1], and forwards fencing requests accordingly. This satisifies most of the requirements, but adds a conundrum when configuring guest clusters, as /dev/ttySX may be /dev/ttySY on another guest. So, either we must account for this per-guest configuration discrepancy or we must make it an administrative requirement to provide the same serial device on each host * Multicast. This violates the networking requirement, but this is okay since this method of operation is optional. This operational mode provides for one of the simpler configurations: all that is needed is the guest's name or UUID. The guest to host communications operates in the same manner as fence_xvm/fence_xvmd, except that there is an implied requirement on restricting the multicast packets accepted to be from the local guests. * VM Channel over Serial. This works like direct serial, but instead of owning the whole device, the device may be shared between multiple applications. The server subscribes to a channel and listens for fencing requests on the channel; the client in the guest OS connects to the channel and issues fencing requests across it. One interesting thing is that it may be possible to provide unprivileged users the ability to fence using this method (I do not claim to know if this is useful or not). Host to Hypervisor interaction ------------------------------ Similar to the way we have plugins for guest to host interaction, we also have plugins which actually do the real work. These plugins are responsible for all of the actual real work performed, including tracking VMs if required, forwarding requests to the appropriate hosts or management services, and handling the responses. We propose at 5 plugins in this case: * Libvirt (local-only). There is no intracommunication and no migration support is provided * Cluster CPG (+ libvirt). This the way fence_xvmd operates today. This setup has the most requirements on the infrastructure, as it requires guest to host networking _and_ host-to-host clustering in order to keep track of virtual machines. The benefit is that it is self-contained and requires no external management nodes. VM states are stored so that other CPG group members know the locations of other VMs and can make some decisions about whether a VM is dead based on whether a host is dead (i.e. if fencing is in use or can be performed on the host). * Libvirt-QMF ... ??? Subscription to the appropriate cluster specific AMQP channel is required on the host side, but this handles routing the message very easily. The fencing request is forwarded to the other listeners on the channel, the VM owner takes the action requested and returns a value. When new VMs are created, the event is broadcast out via the AMQP channel so other hosts know the locations of other VMs and can make some decisions about whether a VM is dead based on whether a host is dead (i.e. if fencing is in use or can be performed on the host). * oVirt Manager. The request is forwarded to the oVirt Manager and the oVirt manager is responsible for taking the appropriate action and responding to the request. * RHEV-M. The request is forwarded to the RHEV-M node, which is responsible for taking the appropriate action and responding to the request. These plugins have no requirements on which guest to host communication plugin is used (you could, if you wanted, use 'direct serial' with 'cluster cpg', or 'multicast' with 'RHEV-H' for example). These plugins must also be able to discover where appropriate. For example, the cpg plugin can only be used if corosync/openais is running. A defined plugin preference order should be specified/documented so that the host daemon behaves in a predictable manner in absence of host-side configuration data (about which plugin to use). [1] TCP was also explored, however, the security is much better using a Unix domain socket, despite the additional complexity of listening for VM creation events. fence-virt-1.0.0/fence_virtd.init.in000066400000000000000000000040411356743026700173500ustar00rootroot00000000000000#!/bin/sh # # fence_virtd - Virtual Machine Fencing host daemon # # chkconfig: 2345 98 01 # description: Fence_virtd is a fencing host daemon which routes fencing \ # requests from clusters of virtual machines to various \ # management layers. # ### BEGIN INIT INFO # Provides: fence_virtd # Required-Start: $network # Required-Stop: # Should-Start: libvirtd # Should-Stop: # Default-Start: # Default-Stop: # Short-Description: start and stop fence_virtd # description: Fence_virtd handles fencing requests from VMs ### end init info # Source function library. . /etc/rc.d/init.d/functions # # Macros generated by configure # prog="fence_virtd" prefix="@prefix@" exec_prefix="@exec_prefix@" exec="@sbindir@/$prog" initconfdir="@initconfdir@" [ -e $initconfdir/$prog ] && . $initconfdir/$prog [ -z "$FENCE_VIRTD_ARGS" ] && FENCE_VIRTD_ARGS="-w" lockfile=/var/lock/subsys/$prog start() { [ -x $exec ] || exit 5 [ -f $config ] || exit 6 echo -n $"Starting $prog: " daemon $exec $FENCE_VIRTD_ARGS retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc $prog retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { stop start } reload() { restart } force_reload() { restart } rh_status() { # run checks to determine if the service is running or use generic status status $prog } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 restart ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 2 esac exit $? fence-virt-1.0.0/fence_virtd.service.in000066400000000000000000000006711356743026700200520ustar00rootroot00000000000000[Unit] Description=Fence-Virt system host daemon Documentation=man:fence_virtd(8) Documentation=man:fence_virt.con(5) After=basic.target After=network.target After=syslog.target After=libvirt-qmf.service After=libvirtd.service After=corosync.service Requires=basic.target Requires=network.target [Install] WantedBy=multi-user.target [Service] Type=forking Restart=on-failure Environment="FENCE_VIRTD_ARGS=-w" # Autogenerated below here fence-virt-1.0.0/include/000077500000000000000000000000001356743026700152125ustar00rootroot00000000000000fence-virt-1.0.0/include/bcast.h000066400000000000000000000006551356743026700164650ustar00rootroot00000000000000#ifndef _XVM_MCAST_H #define _XVM_MCAST_H #define IPV4_MCAST_DEFAULT "225.0.0.12" #define IPV6_MCAST_DEFAULT "ff05::3:1" int ipv4_recv_sk(char *addr, int port); int ipv4_send_sk(char *src_addr, char *addr, int port, struct sockaddr *src, socklen_t slen, int ttl); int ipv6_recv_sk(char *addr, int port); int ipv6_send_sk(char *src_addr, char *addr, int port, struct sockaddr *src, socklen_t slen, int ttl); #endif fence-virt-1.0.0/include/client.h000066400000000000000000000003501356743026700166370ustar00rootroot00000000000000#ifndef _CLIENT_H #define _CLIENT_H int tcp_fence_virt(fence_virt_args_t *args); int serial_fence_virt(fence_virt_args_t *args); int mcast_fence_virt(fence_virt_args_t *args); int vsock_fence_virt(fence_virt_args_t *args); #endif fence-virt-1.0.0/include/debug.h000066400000000000000000000016171356743026700164560ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2007 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _DBG_H #define _DBG_H void dset(int); int dget(void); #define dbg_printf(level, fmt, args...) \ do { \ if (dget()>=level) \ printf(fmt, ##args); \ } while(0) #endif fence-virt-1.0.0/include/fdops.h000066400000000000000000000006141356743026700164770ustar00rootroot00000000000000#ifndef _FDOPS_H #define _FDOPS_H #include #include #include #include int _select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds, struct timeval *timeout); ssize_t _write_retry(int fd, void *buf, int count, struct timeval * timeout); ssize_t _read_retry(int sockfd, void *buf, int count, struct timeval * timeout); #endif fence-virt-1.0.0/include/history.h000066400000000000000000000011121356743026700170570ustar00rootroot00000000000000#ifndef _HISTORY_H #define _HISTORY_H typedef struct _history_node { list_head(); void *data; time_t when; } history_node; typedef int (*history_compare_fn)(void *, void *); typedef struct _history_info { history_node *hist; history_compare_fn compare_func; time_t timeout; size_t element_size; } history_info_t; history_info_t *history_init(history_compare_fn func, time_t expiration, size_t element_size); int history_check(history_info_t *hinfo, void *stuff); int history_record(history_info_t *hinfo, void *data); int history_wipe(history_info_t *hinfo); #endif fence-virt-1.0.0/include/ip_lookup.h000066400000000000000000000023361356743026700173700ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2004,2006 The Magma Cluster API Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The Magma Cluster API Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /** @file * Header for ip_lookup.c */ #ifndef _IP_LOOKUP_H #define _IP_LOOKUP_H #include typedef struct _ip_address { TAILQ_ENTRY(_ip_address) ipa_entries; char ipa_family; char *ipa_address; } ip_addr_t; typedef TAILQ_HEAD(_ip_list, _ip_address) ip_list_t; int ip_search(ip_list_t *ipl, char *ip_name); int ip_free_list(ip_list_t *ipl); int ip_build_list(ip_list_t *ipl); int ip_lookup(char *, struct addrinfo **); #endif fence-virt-1.0.0/include/list.h000066400000000000000000000040541356743026700163410ustar00rootroot00000000000000#ifndef _LIST_H #define _LIST_H /** Simple list handlig macros. Needs rewrite or inclusion of /usr/include/linux/list.h as a replacement. */ /* Must be first if structure is going to use it. */ struct list_entry { struct list_entry *le_next, *le_prev; }; #define list_head() struct list_entry _list_head #define le(p) (&((*p)._list_head)) #define list_insert(list, newnode) \ do { \ if (!(*list)) { \ le(newnode)->le_next = \ le(newnode)->le_prev = le(newnode); \ *list = (void *)le(newnode); \ } else { \ le(*list)->le_prev->le_next = le(newnode); \ le(newnode)->le_next = le(*list); \ le(newnode)->le_prev = le(*list)->le_prev; \ le(*list)->le_prev = le(newnode); \ } \ } while (0) #define list_prepend(list, newnode) \ do { \ list_insert(list, newnode); \ *list = newnode; \ } while (0) #define list_remove(list, oldnode) \ do { \ if (le(oldnode) == le(*list)) { \ *list = (void *)le(*list)->le_next; \ } \ if (le(oldnode) == le(*list)) { \ le(oldnode)->le_next = NULL; \ le(oldnode)->le_prev = NULL; \ *list = NULL; \ } else { \ le(oldnode)->le_next->le_prev = le(oldnode)->le_prev; \ le(oldnode)->le_prev->le_next = le(oldnode)->le_next; \ le(oldnode)->le_prev = NULL; \ le(oldnode)->le_next = NULL; \ } \ } while (0) /* * list_for(list, tmp, counter) { * stuff; * } * * counter = # of items in list when done. * * sets cnt to 0 before even checking list; * * checks for valid list * * traverses list, incrementing counter. If we get to the for loop, * there must be at least one item in the list * * cnt ends up being the number of items in the list. */ #define list_for(list, curr, cnt) \ if ((!(cnt=0)) && (*list != NULL)) \ for (curr = *list; \ (cnt == 0) || (curr != *list); \ curr = (void*)le(curr)->le_next, \ cnt++) #define list_for_rev(list, curr, cnt) \ if ((!(cnt=0)) && (*list != NULL)) \ for (curr = (void *)(le(*list)->le_prev); \ (cnt == 0) || ((void *)curr != le(*list)->le_prev); \ curr = (void*)(le(curr)->le_prev), \ cnt++) #endif fence-virt-1.0.0/include/mcast.h000066400000000000000000000022351356743026700164740ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _XVM_MCAST_H #define _XVM_MCAST_H #define IPV4_MCAST_DEFAULT "225.0.0.12" #define IPV6_MCAST_DEFAULT "ff05::3:1" int ipv4_recv_sk(char *addr, int port, unsigned int ifindex); int ipv4_send_sk(char *src_addr, char *addr, int port, struct sockaddr *src, socklen_t slen); int ipv6_recv_sk(char *addr, int port, unsigned int ifindex); int ipv6_send_sk(char *src_addr, char *addr, int port, struct sockaddr *src, socklen_t slen); #endif fence-virt-1.0.0/include/options.h000066400000000000000000000045201356743026700170570ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _XVM_OPTIONS_H #define _XVM_OPTIONS_H typedef enum { F_FOREGROUND = 0x1, F_NOCCS = 0x2, F_ERR = 0x4, F_HELP = 0x8, F_USE_UUID = 0x10, F_VERSION = 0x20, F_CCSERR = 0x40, F_CCSFAIL = 0x80, F_NOCLUSTER = 0x100 } arg_flags_t; typedef enum { MODE_MULTICAST = 0, /*MODE_BROADCAST = 1,*/ MODE_SERIAL = 2, MODE_VMCHANNEL = 3, MODE_TCP = 4, MODE_VSOCK = 5 } client_mode_t; typedef struct { char *domain; fence_cmd_t op; client_mode_t mode; int debug; int timeout; int delay; int retr_time; arg_flags_t flags; struct network_args { char *addr; char *ipaddr; char *key_file; uint32_t cid; int port; fence_hash_t hash; fence_auth_type_t auth; int family; unsigned int ifindex; } net; struct serial_args { char *device; /* Serial device */ char *speed; char *address; /* vmchannel IP */ } serial; } fence_virt_args_t; /* Private structure for commandline / stdin fencing args */ struct arg_info { char opt; char *opt_desc; char *stdin_opt; int eh; char *content_type; char *default_value; char *desc; void (*assign)(fence_virt_args_t *, struct arg_info *, char *); }; /* Get options */ void args_init(fence_virt_args_t *args); void args_finalize(fence_virt_args_t *args); void args_get_getopt(int argc, char **argv, const char *optstr, fence_virt_args_t *args); void args_get_stdin(const char *optstr, fence_virt_args_t *args); void args_get_ccs(const char *optstr, fence_virt_args_t *args); void args_usage(char *progname, const char *optstr, int print_stdin); void args_print(fence_virt_args_t *args); void args_metadata(char *progname, const char *optstr); #endif fence-virt-1.0.0/include/server_plugin.h000066400000000000000000000071631356743026700202560ustar00rootroot00000000000000/* */ #ifndef _SERVER_PLUGIN_H #define _SERVER_PLUGIN_H #include "config.h" #define PLUGIN_VERSION_LISTENER ((double)0.3) #define PLUGIN_VERSION_BACKEND ((double)0.2) #define LISTENER_VER_SYM listener_plugin_version #define BACKEND_VER_SYM backend_plugin_version #define LISTENER_INFO_SYM listener_plugin_info #define BACKEND_INFO_SYM backend_plugin_info #define LISTENER_VER_STR "listener_plugin_version" #define BACKEND_VER_STR "backend_plugin_version" #define LISTENER_INFO_STR "listener_plugin_info" #define BACKEND_INFO_STR "backend_plugin_info" typedef void * listener_context_t; typedef void * backend_context_t; /* These callbacks hand requests off to the appropriate backend. */ /* Do nothing. Returns 1 (failure) to caller */ typedef int (*fence_null_callback)(const char *vm_name, void *priv); /* Turn the VM 'off'. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_off_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Turn the VM 'on'. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_on_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Reboot a VM. Returns 0 to caller if successful or nonzero if unsuccessful. */ typedef int (*fence_reboot_callback)(const char *vm_name, const char *src, uint32_t seqno, void *priv); /* Get status of a VM. Returns 0 to caller if VM is alive or nonzero if VM is not alive. */ typedef int (*fence_status_callback)(const char *vm_name, void *priv); /* Get status of backend. Returns 0 to caller if backend is responding to requests. */ typedef int (*fence_devstatus_callback)(void *priv); /* VMs available to fence. Returns 0 to caller if backend is responding to requests and a host list can be produced */ typedef int (*hostlist_callback)(const char *vm_name, const char *uuid, int state, void *arg); typedef int (*fence_hostlist_callback)(hostlist_callback cb, void *arg, void *priv); typedef int (*backend_init_fn)(backend_context_t *c, config_object_t *config); typedef int (*backend_cleanup_fn)(backend_context_t c); typedef struct _fence_callbacks { fence_null_callback null; fence_off_callback off; fence_on_callback on; fence_reboot_callback reboot; fence_status_callback status; fence_devstatus_callback devstatus; fence_hostlist_callback hostlist; } fence_callbacks_t; typedef struct backend_plugin { const char *name; const char *version; const fence_callbacks_t *callbacks; backend_init_fn init; backend_cleanup_fn cleanup; } backend_plugin_t; typedef int (*listener_init_fn)(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv); typedef int (*listener_dispatch_fn)(listener_context_t c, struct timeval *timeout); typedef int (*listener_cleanup_fn)(listener_context_t c); typedef struct listener_plugin { const char *name; const char *version; listener_init_fn init; listener_dispatch_fn dispatch; listener_cleanup_fn cleanup; } listener_plugin_t; typedef enum { PLUGIN_NONE = 0, PLUGIN_LISTENER = 1, PLUGIN_BACKEND = 2 } plugin_type_t; #ifdef __cplusplus extern "C" { #endif int plugin_reg_backend(const backend_plugin_t *plugin); int plugin_reg_listener(const listener_plugin_t *plugin); const backend_plugin_t *plugin_find_backend(const char *name); const listener_plugin_t *plugin_find_listener(const char *name); void plugin_dump(void); #ifdef _MODULE int plugin_load(const char *filename); int plugin_search(const char *pathname); #endif #ifdef __cplusplus } #endif #endif fence-virt-1.0.0/include/simple_auth.h000066400000000000000000000023431356743026700176770ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _XVM_SIMPLE_AUTH_H #define _XVM_SIMPLE_AUTH_H #include /* 2-way challenge/response simple auth */ #define DEFAULT_KEY_FILE "/etc/cluster/fence_xvm.key" int read_key_file(char *, char *, size_t); int sock_challenge(int, fence_auth_type_t, void *, size_t, int); int sock_response(int, fence_auth_type_t, void *, size_t, int); int sign_request(fence_req_t *, void *, size_t); int verify_request(fence_req_t *, fence_hash_t, void *, size_t); /* SSL certificate-based authentication TBD */ #endif fence-virt-1.0.0/include/simpleconfig.h000066400000000000000000000026311356743026700200440ustar00rootroot00000000000000#ifndef _SIMPLECONFIG_H #define _SIMPLECONFIG_H typedef int (*config_get_t)(void *config, const char *key, char *value, size_t valuesz); typedef int (*config_set_t)(void *config, const char *key, const char *value); typedef int (*config_parse_t)(const char *filename, void **config); typedef int (*config_free_t)(void *config); typedef void (*config_dump_t)(void *config, FILE *fp); /* * We use an abstract object here so we do not have to link loadable * modules against the configuration library. */ typedef struct { config_get_t get; config_set_t set; config_parse_t parse; config_free_t free; config_dump_t dump; void *info; } config_object_t; /* * These macros may be called from within a loadable module */ #define sc_get(obj, key, value, valuesz) \ obj->get(obj->info, key, value, valuesz) #define sc_set(obj, key, value) \ obj->set(obj->info, key, value) #define sc_parse(obj, filename) \ obj->parse(filename, &obj->info) #define sc_free(obj) \ obj->free(obj->info) #define sc_dump(obj, fp) \ obj->dump(obj->info, fp) /* * Do not call the below functions from loadable modules. Doing so * requires linking the configuration library in to the modules, which * is what we want to avoid. */ /* Returns a copy of our simple config object */ config_object_t *sc_init(void); /* Frees a previously-allocated copy of our simple config object */ void sc_release(config_object_t *c); #endif fence-virt-1.0.0/include/static_map.h000066400000000000000000000013641356743026700175130ustar00rootroot00000000000000#ifndef _STATIC_MAP_H #define _STATIC_MAP_H typedef int (*map_load_t)(void *config, void **perm_info); typedef int (*map_check_t)(void *info, const char *src, const char *tgt); typedef void (*map_cleanup_t)(void **info); typedef struct { map_load_t load; map_check_t check; map_cleanup_t cleanup; void *info; } map_object_t; /* * These macros may be called from within a loadable module */ #define map_load(obj, config) \ obj->load(config, &obj->info) #define map_check(obj, src, tgt) \ obj->check(obj->info, src, tgt) #define map_free(obj) \ obj->cleanup(obj->info) /* Returns a copy of our simple config object */ void *map_init(void); /* Frees a previously-allocated copy of our simple config object */ void map_release(void *c); #endif fence-virt-1.0.0/include/tcp.h000066400000000000000000000020411356743026700161460ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _XVM_TCP_H #define _XVM_TCP_H int ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout); int ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout); int ipv4_listen(const char *addr_str, uint16_t port, int backlog); int ipv6_listen(const char *addr_str, uint16_t port, int backlog); #endif fence-virt-1.0.0/include/tcp_listener.h000066400000000000000000000002111356743026700200500ustar00rootroot00000000000000#ifndef __TCP_LISTENER_H #define __TCP_LISTENER_H #define IPV4_TCP_ADDR_DEFAULT "127.0.0.1" #define IPV6_TCP_ADDR_DEFAULT "::1" #endif fence-virt-1.0.0/include/xvm.h000066400000000000000000000106371356743026700162040ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _XVM_H #define _XVM_H #include #include #include #include #include #define XVM_VERSION "1.9.0" #define MAX_DOMAINNAME_LENGTH 64 /* XXX MAXHOSTNAMELEN */ #define MAX_ADDR_LEN sizeof(struct sockaddr_in6) #define DOMAIN0NAME "Domain-0" #define DOMAIN0UUID "00000000-0000-0000-0000-000000000000" typedef enum { HASH_NONE = 0x0, /* No packet signing */ HASH_SHA1 = 0x1, /* SHA1 signing */ HASH_SHA256 = 0x2, /* SHA256 signing */ HASH_SHA512 = 0x3 /* SHA512 signing */ } fence_hash_t; #define DEFAULT_HASH HASH_SHA256 typedef enum { AUTH_NONE = 0x0, /* Plain TCP */ AUTH_SHA1 = 0x1, /* Challenge-response (SHA1) */ AUTH_SHA256 = 0x2, /* Challenge-response (SHA256) */ AUTH_SHA512 = 0x3 /* Challenge-response (SHA512) */ /* AUTH_SSL_X509 = 0x10 SSL X509 certificates */ } fence_auth_type_t; #define DEFAULT_AUTH AUTH_SHA256 typedef enum { FENCE_NULL = 0x0, FENCE_OFF = 0x1, /* Turn the VM off */ FENCE_REBOOT = 0x2, /* Hit the reset button */ FENCE_ON = 0x3, /* Turn the VM on */ FENCE_STATUS = 0x4, /* virtual machine status (off/on) */ FENCE_DEVSTATUS = 0x5, /* Status of the fencing device */ FENCE_HOSTLIST = 0x6, /* List VMs controllable */ FENCE_METADATA = 0x7, /* Print fence agent metadata */ FENCE_VALIDATEALL = 0x8 /* Validate command-line or stdin arguments and exit */ } fence_cmd_t; #define DEFAULT_TTL 4 #ifndef DEFAULT_HYPERVISOR_URI #define DEFAULT_HYPERVISOR_URI "qemu:///system" #endif #define MAX_HASH_LENGTH SHA512_LENGTH #define MAX_KEY_LEN 4096 typedef struct __attribute__ ((packed)) _fence_req { uint8_t request; /* Fence request */ uint8_t hashtype; /* Hash type used */ uint8_t addrlen; /* Length of address */ uint8_t flags; /* Special flags */ #define RF_UUID 0x1 /* Flag specifying UUID */ uint8_t domain[MAX_DOMAINNAME_LENGTH]; /* Domain to fence*/ uint8_t address[MAX_ADDR_LEN]; /* We're this IP */ #define DEFAULT_MCAST_PORT 1229 uint16_t port; /* Port we bound to */ uint8_t random[6]; /* Random Data */ uint32_t seqno; /* Request identifier; can be random */ uint32_t family; /* Address family */ uint8_t hash[MAX_HASH_LENGTH]; /* Binary hash */ } fence_req_t; #if __BYTE_ORDER == __BIG_ENDIAN #define swab_fence_req_t(req) \ do { \ (req)->seqno = bswap_32((req)->seqno); \ (req)->family = bswap_32((req)->family); \ (req)->port = bswap_32((req)->port); \ } while(0) #else #define swab_fence_req_t(req) #endif /* for host list */ typedef struct __attribute__ ((packed)) _host_info { uint8_t domain[MAX_DOMAINNAME_LENGTH]; uint8_t uuid[MAX_DOMAINNAME_LENGTH]; uint8_t state; uint8_t pad; } host_state_t; #define DEFAULT_SERIAL_DEVICE "/dev/ttyS1" #define DEFAULT_SERIAL_SPEED "115200,8N1" #define DEFAULT_CHANNEL_IP "10.0.2.179" #define SERIAL_MAGIC 0x61626261 /* endian doesn't matter */ typedef struct __attribute__((packed)) _serial_fence_req { uint32_t magic; uint8_t request; uint8_t flags; uint8_t domain[MAX_DOMAINNAME_LENGTH]; uint32_t seqno; } serial_req_t; #if __BYTE_ORDER == __BIG_ENDIAN #define swab_serial_req_t(req) \ do { \ (req)->magic = bswap_32((req)->magic); \ (req)->seqno = bswap_32((req)->seqno); \ } while(0) #else #define swab_serial_req_t(req) #endif typedef struct __attribute__((packed)) _serial_fense_resp { uint32_t magic; uint8_t response; } serial_resp_t; #if __BYTE_ORDER == __BIG_ENDIAN #define swab_serial_resp_t(req) \ do { \ (req)->magic = bswap_32((req)->magic); \ } while(0) #else #define swab_serial_resp_t(req) #endif #define RESP_SUCCESS 0 #define RESP_FAIL 1 #define RESP_OFF 2 #define RESP_PERM 3 #define RESP_HOSTLIST 253 #endif fence-virt-1.0.0/m4/000077500000000000000000000000001356743026700141075ustar00rootroot00000000000000fence-virt-1.0.0/m4/ax_check_link_flag.m4000066400000000000000000000061571356743026700201350ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_LINK_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 5 AC_DEFUN([AX_CHECK_LINK_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS fence-virt-1.0.0/m4/ax_prog_date.m4000066400000000000000000000114241356743026700170070ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_prog_date.html # =========================================================================== # # SYNOPSIS # # AX_PROG_DATE() # # DESCRIPTION # # This macro tries to determine the type of the date (1) command and some # of its non-standard capabilities. # # The type is determined as follow: # # * If the version string contains "GNU", then: # - The variable ax_cv_prog_date_gnu is set to "yes". # - The variable ax_cv_prog_date_type is set to "gnu". # # * If date supports the "-v 1d" option, then: # - The variable ax_cv_prog_date_bsd is set to "yes". # - The variable ax_cv_prog_date_type is set to "bsd". # # * If both previous checks fail, then: # - The variable ax_cv_prog_date_type is set to "unknown". # # The following capabilities of GNU date are checked: # # * If date supports the --date arg option, then: # - The variable ax_cv_prog_date_gnu_date is set to "yes". # # * If date supports the --utc arg option, then: # - The variable ax_cv_prog_date_gnu_utc is set to "yes". # # The following capabilities of BSD date are checked: # # * If date supports the -v 1d option, then: # - The variable ax_cv_prog_date_bsd_adjust is set to "yes". # # * If date supports the -r arg option, then: # - The variable ax_cv_prog_date_bsd_date is set to "yes". # # All the aforementioned variables are set to "no" before a check is # performed. # # LICENSE # # Copyright (c) 2017 Enrico M. Crisostomo # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 3 AC_DEFUN([AX_PROG_DATE], [dnl AC_CACHE_CHECK([for GNU date], [ax_cv_prog_date_gnu], [ ax_cv_prog_date_gnu=no if date --version 2>/dev/null | head -1 | grep -q GNU then ax_cv_prog_date_gnu=yes fi ]) AC_CACHE_CHECK([for BSD date], [ax_cv_prog_date_bsd], [ ax_cv_prog_date_bsd=no if date -v 1d > /dev/null 2>&1 then ax_cv_prog_date_bsd=yes fi ]) AC_CACHE_CHECK([for date type], [ax_cv_prog_date_type], [ ax_cv_prog_date_type=unknown if test "x${ax_cv_prog_date_gnu}" = "xyes" then ax_cv_prog_date_type=gnu elif test "x${ax_cv_prog_date_bsd}" = "xyes" then ax_cv_prog_date_type=bsd fi ]) AS_VAR_IF([ax_cv_prog_date_gnu], [yes], [ AC_CACHE_CHECK([whether GNU date supports --date], [ax_cv_prog_date_gnu_date], [ ax_cv_prog_date_gnu_date=no if date --date=@1512031231 > /dev/null 2>&1 then ax_cv_prog_date_gnu_date=yes fi ]) AC_CACHE_CHECK([whether GNU date supports --utc], [ax_cv_prog_date_gnu_utc], [ ax_cv_prog_date_gnu_utc=no if date --utc > /dev/null 2>&1 then ax_cv_prog_date_gnu_utc=yes fi ]) ]) AS_VAR_IF([ax_cv_prog_date_bsd], [yes], [ AC_CACHE_CHECK([whether BSD date supports -r], [ax_cv_prog_date_bsd_date], [ ax_cv_prog_date_bsd_date=no if date -r 1512031231 > /dev/null 2>&1 then ax_cv_prog_date_bsd_date=yes fi ]) ]) AS_VAR_IF([ax_cv_prog_date_bsd], [yes], [ AC_CACHE_CHECK([whether BSD date supports -v], [ax_cv_prog_date_bsd_adjust], [ ax_cv_prog_date_bsd_adjust=no if date -v 1d > /dev/null 2>&1 then ax_cv_prog_date_bsd_adjust=yes fi ]) ]) ])dnl AX_PROG_DATE fence-virt-1.0.0/m4/ax_pthread.m4000066400000000000000000000505221356743026700164740ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 24 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD fence-virt-1.0.0/man/000077500000000000000000000000001356743026700143425ustar00rootroot00000000000000fence-virt-1.0.0/man/Makefile.am000066400000000000000000000013261356743026700164000ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(man8_MANS) $(man5_MANS) man8_MANS = fence_virt.8 fence_xvm.8 fence_virtd.8 man5_MANS = fence_virt.conf.5 fence-virt-1.0.0/man/fence_virt.8000066400000000000000000000140261356743026700165620ustar00rootroot00000000000000.TH FENCE_AGENT 8 2009-12-04 "fence_virt (Fence Agent)" .SH NAME fence_virt - Fencing agent for virtual machines using VM Channel fence_xvm - Fencing agent for virtual machines using multicast .SH DESCRIPTION .P fence_virt and fence_xvm are an I/O Fencing agents which can be used with virtual machines. Fence_xvm is backward compatible with fence_xvmd. Fence_virt and fence_xvm talk to fence_virtd, which supports multiple backend plugins, including: - libvirt for single-node operation - Corosync CPG when using Linux-cluster release 3.0.0 or later - libvirt-qpid for multi-node, non-cluster operation For compatibility, fence_xvm may talk to fence_xvmd from linux-cluster release 2 or later. .P fence_virt and fence_xvm accept options on the command line as well as from stdin. The fencing daemon sends parameters through stdin when it execs the agent. The agent may be run by itself with command line options, which is useful for testing or turning on or off virtual machines from scripts. .SH GENERAL PARAMETERS .TP .B -d . Increment (command line) debug level .TP .B -H . Virtual machine (domain UUID or name) to fence .TP .B -o . Fencing action (null, off, on, reboot, status, monitor, list, list-status, validate-all, or metadata) (Default Value: reboot). See the FENCING ACTIONS section. .TP .B -t . Fencing timeout (in seconds) (Default Value: 30) .TP .B -w . Fencing delay (in seconds). The fence agent will wait the specified number of seconds before attempting a fencing operation. (Default Value: 0) .SH MULTICAST PARAMETERS These parameters are used only when using fence_virt in multicast mode (e.g. by running fence_xvm). .TP .B -i . IP Family (auto, ipv4, ipv6) (Default Value: auto) .TP .B -a . Multicast address (Default Values: 225.0.0.12 / ff02::3:1) .TP .B -p . IP port (Default Value: 1229) .TP .B -T . Multicast time-to-live (in hops) (Default Value: 2) .TP .B -r . Multicast retransmit time (in 1/10sec) (Default Value: 20) .TP .B -C . Authentication (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B -c . Packet hash strength (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B -k . Shared key file (Default Value: /etc/cluster/fence_xvm.key) .TP .B -u . Specify if using fence_virt with older fence_xvmd installations which are using UUIDs. Fence_virtd ignores this parameter. .SH SERIAL/VMCHANNEL/TCP/VSOCK PARAMETERS These parameters are used only when using fence_virt in serial mode (e.g. by running fence_virt). .TP .B -D . Serial device (fence_virt mode). On the host, the serial device must be mapped in each domain's configuration file. See fence_virt.conf(5) for more information. Specifying a serial device causes fence_virt to use serial mode (as opposed to VMChannel mode). .TP .B -P . Serial parameters. (Default Value: 115200,8N1) .TP .B -A VMChannel IP address (Default Value: 10.0.2.179) .TP .B -p . VMChannel IP port (Default Value: 1229) .TP .B -S [] . Connect to the fence_virt daemon via vsock. If CID is specified, fence_virt will attempt to connect to the specified CID, otherwise it will connect to the default hypervisor CID of 2. .SH GENERAL STDIN PARAMETERS These parameters are passed to fence_virt via standard input if no command line options are present. They are presented to fence_virt in the form: name=value .TP .B debug . Specify debugging level .TP .B port . Virtual machine (domain UUID or name) to fence .TP .B action .TP .B action . Fencing action (null, off, on, reboot, status, monitor, list, list-status, validate-all, or metadata) (Default Value: reboot). See the FENCING ACTIONS section. .TP .B timeout . Fencing timeout (in seconds) (Default Value: 30) .SH MULTICAST STDIN PARAMETERS .TP .B ip_family . IP Family ([auto], ipv4, ipv6) (Default Value: auto) .TP .B multicast_address . Multicast address (Defaults: 225.0.0.12 / ff02::3:1) .TP .B port . IP port (Default Value: 1229) .TP .B multicast_ttl . Multicast time-to-live (in hops) (Default Value: 2) .TP .B retrans . Multicast retransmit time (in 1/10sec) (Default Value: 20) .TP .B auth . Authentication (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B hash . Packet hash strength (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B key_file . Shared key file (Default Value: /etc/cluster/fence_xvm.key) .TP .B use_uuid . Specify if using fence_virt with older fence_xvmd installations which are using UUIDs. Fence_virtd ignores this parameter. .SH SERIAL/VMCHANNEL/TCP/VSOCK STDIN PARAMETERS .TP .B serial_device . Serial device. On the host, the serial device must be mapped in each domain's configuration file. See fence_virt.conf(5) for more information. If specified, causes fence_virt to operate in serial mode (not specifying causes fence_virt to operate in VM Channel mode). .TP .B serial_params . Serial parameters. Default=115200,8N1. .TP .B channel_ip . Channel IP. Default=10.0.2.179 .TP .B ipport . Channel port. Default=1229 .TP .B vsock . Connect to the fence_virt daemon via vsock. If an argument is given, fence_virt will use the argument specified as the CID for the connection. If no argument is given, it will connect to the default hypervisor CID of 2. .SH FENCING ACTIONS .TP \fBoff \fP Destroy or turn off virtual machine. .TP \fBreboot \fP Reboot virtual machine; i.e. restart the virtual machine on the same host where it is currently running. .TP \fBon \fP Turn on a virtual machine. Note: Turning on a virtual machine only works with some fence_virt backends. .TP \fBstatus \fP Check whether a virtual machine is running or not. .TP \fBmonitor \fP Check the health of fence_virtd's backend plugin. .TP \fBlist\fP and \fBlist-status\fP List virtual machines which may be fenced by fence_virtd (not supported on all backends). .TP \fBvalidate-all\fP Validate arguments given on either the command line or standard input. If validation fails, the fence agent will exit with code 1, otherwise it will exit with code 0. .TP \fBmetadata \fP Print XML metadata to standard output. .SH SEE ALSO fence_virtd(8), fence(8), fence_virtd.conf(5) fence-virt-1.0.0/man/fence_virt.conf.5000066400000000000000000000171021356743026700175010ustar00rootroot00000000000000.TH fence_virtd.conf 5 .SH NAME fence_virt.conf - configuration file for fence_virtd .SH DESCRIPTION The fence_virt.conf file contains configuration information for fence_virtd, a fencing request routing daemon for clusters of virtual machines. The file is tree-structured. There are parent/child relationships and sibling relationships between the nodes. foo { bar { baz = "1"; } } There are three primary sections of fence_virt.conf. .SH SECTIONS .SS fence_virtd This section contains global information about how fence_virtd is to operate. The most important pieces of information are as follows: .TP .B listener . the listener plugin for receiving fencing requests from clients .TP .B backend . the plugin to be used to carry out fencing requests .TP .B foreground . do not fork into the background. .TP .B wait_for_init . wait for the frontend and backends to become available rather than giving up immediately. This replaces wait_for_backend in 0.2.x. .TP .B module_path . the module path to search for plugins .SS listeners This section contains listener-specific configuration information; see the section about listeners below. .SS backends This section contains listener-specific configuration information; see the section about listeners below. .SS groups This section contains static maps of which virtual machines may fence which other virtual machines; see the section about groups below. .SH LISTENERS There are various listeners available for fence_virtd, each one handles decoding and authentication of a given fencing request. The following configuration blocks belong in the \fBlisteners\fP section of fence_virt.conf .SS multicast .TP .B key_file . the shared key file to use (default: /etc/cluster/fence_xvm.key). .TP .B hash . the weakest hashing algorithm allowed for client requests. Clients may send packets with stronger hashes than the one specified, but not weaker ones. (default: sha256, but could be sha1, sha512, or none) .TP .B auth . the hashing algorithm to use for the simplistic challenge-response authentication (default: sha256, but could be sha1, sha512, or none) .TP .B family . the IP family to use (default: ipv4, but may be ipv6) .TP .B address . the multicast address to listen on (default: 225.0.0.12) .TP .B port . the multicast port to listen on (default: 1229) .TP .B interface . interface to listen on. By default, fence_virtd listens on all interfaces. However, this causes problems in some environments where the host computer is used as a gateway. .SS serial The serial listener plugin utilizes libvirt's serial (or VMChannel) mapping to listen for requests. When using the serial listener, it is necessary to add a serial port (preferably pointing to /dev/ttyS1) or a channel (preferably pointing to 10.0.2.179:1229) to the libvirt domain description. Note that only type .B unix , mode .B bind serial ports and channels are supported. Example libvirt XML: .in 8 <\fBserial\fP type='unix'> <\fBchannel\fP type='unix'> .in 0 .TP .B uri . the URI to use when connecting to libvirt by the serial plugin. .TP .B path . The same directory that is defined for the domain serial port path (From example above: /sandbox/guests). Sockets must reside in this directory in order to be considered valid. This can be used to prevent fence_virtd from using the wrong sockets. .TP .B mode . This selects the type of sockets to register. Valid values are "serial" (default) and "vmchannel". .SS tcp The tcp listener operates similarly to the multicast listener but uses TCP sockets for communication instead of using multicast packets. .TP .B key_file . the shared key file to use (default: /etc/cluster/fence_xvm.key). .TP .B hash . the hashing algorithm to use for packet signing (default: sha256, but could be sha1, sha512, or none) .TP .B auth . the hashing algorithm to use for the simplistic challenge-response authentication (default: sha256, but could be sha1, sha512, or none) .TP .B family . the IP family to use (default: ipv4, but may be ipv6) .TP .B address . the IP address to listen on (default: 127.0.0.1 for IPv4, ::1 for IPv6) .TP .B port . the TCP port to listen on (default: 1229) .SS vsock The vsock listener operates similarly to the multicast listener but uses virtual machine sockets (AF_VSOCK) for communication instead of using multicast packets. .TP .B key_file . the shared key file to use (default: /etc/cluster/fence_xvm.key). .TP .B hash . the hashing algorithm to use for packet signing (default: sha256, but could be sha1, sha512, or none) .TP .B auth . the hashing algorithm to use for the simplistic challenge-response authentication (default: sha256, but could be sha1, sha512, or none) .TP .B port . the vsock port to listen on (default: 1229) .SH BACKENDS There are various backends available for fence_virtd, each one handles routing a fencing request to a hypervisor or management tool. The following configuration blocks belong in the \fBbackends\fP section of fence_virt.conf .SS libvirt The libvirt plugin is the simplest plugin. It is used in environments where routing fencing requests between multiple hosts is not required, for example by a user running a cluster of virtual machines on a single desktop computer. .TP .B uri . the URI to use when connecting to libvirt. .SS libvirt-qmf The libvirt-qmf plugin acts as a QMFv2 Console to the libvirt-qmf daemon in order to route fencing requests over AMQP to the appropriate computer. .TP .B host . host or IP address of qpid broker. Defaults to 127.0.0.1. .TP .B port . IP port of qpid broker. Defaults to 5672. .TP .B username . Username for GSSAPI, if configured. .TP .B service . Qpid service to connect to. .TP .B gssapi . If set to 1, have fence_virtd use GSSAPI for authentication when communicating with the Qpid broker. Default is 0 (off). .SS cpg The cpg plugin uses corosync CPG and libvirt to track virtual machines and route fencing requests to the appropriate computer. .TP .B uri . the URI to use when connecting to libvirt by the cpg plugin. .TP .B name_mode . The cpg plugin, in order to retain compatibility with fence_xvm, stores virtual machines in a certain way. The default was to use 'name' when using fence_xvm and fence_xvmd, and so this is still the default. However, it is strongly recommended to use 'uuid' instead of 'name' in all cluster environments involving more than one physical host in order to avoid the potential for name collisions. .SH GROUPS Fence_virtd supports static maps which allow grouping of VMs. The groups are arbitrary and are checked at fence time. Any member of a group may fence any other member. Hosts may be assigned to multiple groups if desired. .SS group This defines a group. .TP .B uuid . defines UUID as a member of a group. .TP .B ip . defines an IP which is allowed to send fencing requests for members of this group (e.g. for multicast). It is highly recommended that this be used in conjunction with a key file. .SH EXAMPLE fence_virtd { listener = "multicast"; backend = "cpg"; } # this is the listeners section listeners { multicast { key_file = "/etc/cluster/fence_xvm.key"; } } backends { libvirt { uri = "qemu:///system"; } } groups { group { ip = "192.168.1.1"; uuid = "44179d3f-6c63-474f-a212-20c8b4b25b16"; uuid = "1ce02c4b-dfa1-42cb-b5b1-f0b1091ece60"; } } .SH SEE ALSO fence_virtd(8), fence_virt(8), fence_xvm(8), fence(8) fence-virt-1.0.0/man/fence_virtd.8000066400000000000000000000023401356743026700167220ustar00rootroot00000000000000.TH FENCE_AGENT 8 2010-01-05 "fence_virtd (Fence Agent)" .SH NAME fence_virtd - Fencing host for virtual machines .SH DESCRIPTION .P fence_virtd is a host daemon designed to route fencing requests for virtual machines. Fence_virt and fence_xvm talk to fence_virtd, which supports multiple backend plugins, including: - libvirt for single-node operation - Corosync CPG and libvirt when using Linux-cluster release 3.0.0 or later - libvirt-qpid for multi-node, non-cluster operation For compatibility, fence_xvm from linux-cluster release 2 may talk to fence_virtd. .P fence_virtd accepts a few options on the command line, but most options are read from fence_virt.conf. .SH PARAMETERS .TP .B -d . Specify debug level, e.g. "-d99" .TP .B -c . Interactively prompt user for configuration information .TP .B -f . Specify an alternate configuration file instead of /etc/fence_virt.conf .TP .B -F . Do not fork into background after starting (overrides any setting in fence_virt.conf) .TP .B -w . Wait for backend to be available (overrides any setting in fence_virt.conf) .TP .B -p . Specify the full path to the pid file used to record the active process pid. .SH SEE ALSO fence_virt(8), fence_xvm(8), fence(8), fence_virt.conf(5) fence-virt-1.0.0/man/fence_xvm.8000066400000000000000000000000261356743026700164030ustar00rootroot00000000000000.so man8/fence_virt.8 fence-virt-1.0.0/server/000077500000000000000000000000001356743026700150755ustar00rootroot00000000000000fence-virt-1.0.0/server/Makefile.am000066400000000000000000000114311356743026700171310ustar00rootroot00000000000000############################################################################### ############################################################################### ## ## Copyright (C) 2009-2019 Red Hat, Inc. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2. ## ############################################################################### ############################################################################### MAINTAINERCLEANFILES = Makefile.in noinst_HEADERS = cpg.h serial.h uuid-test.h virt.h sbin_PROGRAMS = fence_virtd # # daemon # fence_virtd_SOURCES = main.c plugin.c config.c static_map.c uuid-test.c \ daemon_init.c fence_virtd_CFLAGS = $(AM_CFLAGS) \ $(nss_CFLAGS) $(xml2_CFLAGS) $(uuid_CFLAGS) $(PTHREAD_CFLAGS) fence_virtd_LDADD = $(CONFIG_LIBS) $(COMMON_LIBS) \ $(nss_LIBS) $(xml2_LIBS) $(uuid_LIBS) $(PTHREAD_LIBS) $(dl_LIBS) fence_virtd_LDFLAGS = $(AM_LDFLAGS) $(COMMON_LDFLAGS) # # modules # virt_la_SOURCES = libvirt.c null_la_SOURCES = null.c virt_qmf_la_SOURCES = libvirt-qmf.cpp pm_fence_la_SOURCES = pm-fence.c cpg_la_SOURCES = cpg-virt.c cpg.c multicast_la_SOURCES = mcast.c tcp_la_SOURCES = tcp.c vsock_la_SOURCES = vsock.c serial_la_SOURCES = virt-serial.c virt-sockets.c serial.c ################# # Modular build # ################# if modularbuild fence_virtd_CFLAGS += -DMODULE_PATH=\"$(pkglibdir)/\" pkglib_LTLIBRARIES = MODULESCFLAGS = $(AM_CFLAGS) MODULESLDFLAGS = $(AM_LDFLAGS) $(COMMON_LIBS) $(COMMON_LDFLAGS) -module -avoid-version -export-dynamic if modlibvirt pkglib_LTLIBRARIES += virt.la virt_la_SOURCES += virt.c uuid-test.c virt_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(virt_CFLAGS) virt_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(virt_LIBS) endif if modlibvirtqmf pkglib_LTLIBRARIES += virt-qmf.la virt_qmf_la_SOURCES += uuid-test.c virt_qmf_la_CXXFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(qpid_CFLAGS) virt_qmf_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(qpid_LIBS) endif if modpmfence pkglib_LTLIBRARIES += pm-fence.la pm_fence_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(cib_CFLAGS) $(glib2_CFLAGS) $(xml2_CFLAGS) pm_fence_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(cib_LIBS) $(glib2_LIBS) $(xml2_LIBS) endif if modcpg pkglib_LTLIBRARIES += cpg.la cpg_la_SOURCES += virt.c history.c uuid-test.c cpg_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(cpg_CFLAGS) $(virt_CFLAGS) cpg_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(cpg_LIBS) $(virt_LIBS) endif if modmulticast pkglib_LTLIBRARIES += multicast.la multicast_la_SOURCES += history.c multicast_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) multicast_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modserial pkglib_LTLIBRARIES += serial.la serial_la_SOURCES += history.c serial_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) $(xml2_CFLAGS) $(virt_CFLAGS) serial_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) $(xml2_LIBS) $(virt_LIBS) endif if modtcp pkglib_LTLIBRARIES += tcp.la tcp_la_SOURCES += history.c tcp_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) tcp_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modvsock pkglib_LTLIBRARIES += vsock.la vsock_la_SOURCES += history.c vsock_la_CFLAGS = $(MODULESCFLAGS) $(nss_CFLAGS) vsock_la_LDFLAGS = $(MODULESLDFLAGS) $(nss_LIBS) endif if modnull pkglib_LTLIBRARIES += null.la null_la_CFLAGS = $(MODULESCFLAGS) null_la_LDFLAGS = $(MODULESLDFLAGS) endif else #################### # Monolithic build # #################### # avoid a linking issues when: # 1) do a server modular build # 2) run configure to switch to monolithic build without make clean # 3) do a server build # # libtool will get confused by the presence of .la files in the builddir # and will fail to link the binary EXTRA_fence_virtd_DEPENDENCIES = cleanmods cleanmods: @rm -rf $(builddir)/*.la fence_virtd_SOURCES += virt.c history.c if modlibvirt fence_virtd_SOURCES += $(libvirt_la_SOURCES) fence_virtd_CFLAGS += $(virt_CFLAGS) fence_virtd_LDADD += $(virt_LIBS) endif if modlibvirtqmf fence_virtd_SOURCES += $(libvirt_qmf_la_SOURCES) fence_virtd_CFLAGS += $(qpid_CFLAGS) fence_virtd_LDADD += $(qpid_LIBS) endif if modpmfence fence_virtd_SOURCES += $(pm_fence_la_SOURCES) fence_virtd_CFLAGS += $(cib_CFLAGS) fence_virtd_LDADD += $(cib_LIBS) endif if modcpg fence_virtd_SOURCES += $(cpg_la_SOURCES) fence_virtd_CFLAGS += $(cpg_CFLAGS) fence_virtd_LDADD += $(cpg_LIBS) endif if modmulticast fence_virtd_SOURCES += $(multicast_la_SOURCES) endif if modserial fence_virtd_SOURCES += $(serial_la_SOURCES) endif if modtcp fence_virtd_SOURCES += $(tcp_la_SOURCES) endif if modvsock fence_virtd_SOURCES += $(vsock_la_SOURCES) endif if modnull fence_virtd_SOURCES += $(null_la_SOURCES) endif endif fence-virt-1.0.0/server/config.c000066400000000000000000000360621356743026700165150ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "simpleconfig.h" #include "static_map.h" #include "mcast.h" #include "xvm.h" #include "server_plugin.h" #include "simple_auth.h" static int yesno(const char *prompt, int dfl) { char result[10]; printf("%s [%c/%c]? ", prompt, dfl?'Y':'y', dfl?'n':'N'); fflush(stdout); memset(result, 0, sizeof(result)); if (fgets(result, 9, stdin) == NULL) return dfl; if (result[0] == 'y' || result[0] == 'Y') return 1; if (result[0] == 'n' || result[0] == 'N') return 0; return dfl; } static int text_input(const char *prompt, char *dfl, char *input, size_t len) { printf("%s [%s]: ", prompt, dfl?dfl:""); fflush(stdout); memset(input, 0, len); if (fgets(input, len, stdin) == NULL) { strncpy(input, dfl, len); return 0; } if (input[strlen(input)-1] == '\n') input[strlen(input)-1] = 0; if (strlen(input) == 0) { strncpy(input, dfl, len); return 0; } return 0; } static int plugin_path_configure(config_object_t *config) { #ifdef _MODULE char val[4096]; char inp[4096]; int done = 0; if (sc_get(config, "fence_virtd/@module_path", val, sizeof(val))) { #ifdef MODULE_PATH snprintf(val, sizeof(val), MODULE_PATH); #else printf("Failed to determine module search path.\n"); #endif } do { text_input("Module search path", val, inp, sizeof(inp)); printf("\n"); done = plugin_search(inp); if (done > 0) { plugin_dump(); done = 1; } else { done = 0; printf("No modules found in %s!\n", inp); if (yesno("Use this value anyway", 0) == 1) done = 1; } } while (!done); sc_set(config, "fence_virtd/@module_path", inp); #endif return 0; } static int backend_config_libvirt(config_object_t *config) { char val[4096]; char inp[4096]; printf("\n"); printf("The libvirt backend module is designed for single desktops or\n" "servers. Do not use in environments where virtual machines\n" "may be migrated between hosts.\n\n"); /* Default backend plugin */ if (sc_get(config, "backends/libvirt/@uri", val, sizeof(val))) { strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val)); } text_input("Libvirt URI", val, inp, sizeof(inp)); sc_set(config, "backends/libvirt/@uri", inp); return 0; } static int backend_config_cpg(config_object_t *config) { char val[4096]; char inp[4096]; int done = 0; printf("\n"); printf("The CPG backend module is designed for use in clusters\n" "running corosync and libvirt. It utilizes the CPG API to \n" "route fencing requests, finally utilizing libvirt to perform\n" "fencing actions.\n\n"); if (sc_get(config, "backends/cpg/@uri", val, sizeof(val))) { strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val)); } text_input("Libvirt URI", val, inp, sizeof(inp)); sc_set(config, "backends/cpg/@uri", inp); printf("\n"); printf("The name mode is how the cpg plugin stores and \n" "references virtual machines. Since virtual machine names\n" "are not guaranteed to be unique cluster-wide, use of UUIDs\n" "is strongly recommended. However, for compatibility with \n" "fence_xvmd, the use of 'name' mode is also supported.\n\n"); if (sc_get(config, "backends/cpg/@name_mode", val, sizeof(val))) { strncpy(val, "uuid", sizeof(val)); } do { text_input("VM naming/tracking mode (name or uuid)", val, inp, sizeof(inp)); if (!strcasecmp(inp, "uuid")) { done = 1; } else if (!strcasecmp(inp, "name")) { done = 0; printf("This can be dangerous if you do not take care to" "ensure that\n" "virtual machine names are unique " "cluster-wide.\n"); if (yesno("Use name mode anyway", 1) == 1) done = 1; } } while (!done); sc_set(config, "backends/cpg/@name_mode", inp); return 0; } static int listener_config_multicast(config_object_t *config) { char val[4096]; char inp[4096]; const char *family = "ipv4"; struct in_addr sin; struct in6_addr sin6; int done = 0; printf("\n"); printf("The multicast listener module is designed for use environments\n" "where the guests and hosts may communicate over a network using\n" "multicast.\n\n"); /* MULTICAST IP ADDRESS/FAMILY */ printf("The multicast address is the address that a client will use to\n" "send fencing requests to fence_virtd.\n\n"); if (sc_get(config, "listeners/multicast/@address", val, sizeof(val)-1)) { strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val)); } done = 0; do { text_input("Multicast IP Address", val, inp, sizeof(inp)); if (inet_pton(AF_INET, inp, &sin) == 1) { printf("\nUsing ipv4 as family.\n\n"); family = "ipv4"; done = 1; } else if (inet_pton(AF_INET6, inp, &sin6) == 1) { printf("\nUsing ipv6 as family.\n\n"); family = "ipv6"; done = 1; } else printf("'%s' is not a valid IP address!\n", inp); } while (!done); sc_set(config, "listeners/multicast/@family", family); sc_set(config, "listeners/multicast/@address", inp); /* MULTICAST IP PORT */ if (sc_get(config, "listeners/multicast/@port", val, sizeof(val)-1)) { snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT); } done = 0; do { char *p; int ret; text_input("Multicast IP Port", val, inp, sizeof(inp)); ret = strtol(inp, &p, 0); if (*p != '\0' || ret <= 0 || ret >= 65536) { printf("Port value '%s' is out of range\n", val); continue; } else done = 1; } while (!done); sc_set(config, "listeners/multicast/@port", inp); /* MULTICAST INTERFACE */ printf("\nSetting a preferred interface causes fence_virtd to listen only\n" "on that interface. Normally, it listens on all interfaces.\n" "In environments where the virtual machines are using the host\n" "machine as a gateway, this *must* be set (typically to virbr0).\n" "Set to 'none' for no interface.\n\n" ); if (sc_get(config, "listeners/multicast/@interface", val, sizeof(val)-1)) { strncpy(val, "none", sizeof(val)); } done = 0; do { text_input("Interface", val, inp, sizeof(inp)); if (!strcasecmp(inp, "none")) { break; } if (strlen(inp) > 0) { int ret; ret = if_nametoindex(inp); if (ret < 0) { printf("Invalid interface: %s\n", inp); if (yesno("Use anyway", 1) == 1) done = 1; } else done = 1; } else printf("No interface given\n"); } while (!done); if (!strcasecmp(inp, "none")) { sc_set(config, "listeners/multicast/@interface", NULL); } else { sc_set(config, "listeners/multicast/@interface", inp); } /* KEY FILE */ printf("\nThe key file is the shared key information which is used to\n" "authenticate fencing requests. The contents of this file must\n" "be distributed to each physical host and virtual machine within\n" "a cluster.\n\n"); if (sc_get(config, "listeners/multicast/@key_file", val, sizeof(val)-1)) { strncpy(val, DEFAULT_KEY_FILE, sizeof(val)); } done = 0; do { text_input("Key File", val, inp, sizeof(inp)); if (!strcasecmp(inp, "none")) { break; } if (strlen(inp) > 0) { if (inp[0] != '/') { printf("Invalid key file: %s\n", inp); if (yesno("Use anyway", 1) == 1) done = 1; } else done = 1; } else printf("No key file given\n"); } while (!done); if (!strcasecmp(inp, "none")) { sc_set(config, "listeners/multicast/@key_file", NULL); } else { sc_set(config, "listeners/multicast/@key_file", inp); } return 0; } static int listener_config_tcp(config_object_t *config) { char val[4096]; char inp[4096]; const char *family = "ipv4"; struct in_addr sin; struct in6_addr sin6; int done = 0; printf("\n"); printf("The TCP listener module is designed for use in environments\n" "where the guests and hosts communicate over viosproxy.\n\n"); /* IP ADDRESS/FAMILY */ printf("The IP address is the address that a client will use to\n" "send fencing requests to fence_virtd.\n\n"); if (sc_get(config, "listeners/tcp/@address", val, sizeof(val)-1)) { strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val)); } done = 0; do { text_input("TCP Listen IP Address", val, inp, sizeof(inp)); if (inet_pton(AF_INET, inp, &sin) == 1) { printf("\nUsing ipv4 as family.\n\n"); family = "ipv4"; done = 1; } else if (inet_pton(AF_INET6, inp, &sin6) == 1) { printf("\nUsing ipv6 as family.\n\n"); family = "ipv6"; done = 1; } else { printf("'%s' is not a valid IP address!\n", inp); continue; } } while (!done); sc_set(config, "listeners/tcp/@family", family); sc_set(config, "listeners/tcp/@address", inp); /* MULTICAST IP PORT */ if (sc_get(config, "listeners/tcp/@port", val, sizeof(val)-1)) { snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT); } done = 0; do { text_input("TCP Listen Port", val, inp, sizeof(inp)); char *p; int ret; ret = strtol(inp, &p, 0); if (*p != '\0' || ret <= 0 || ret >= 65536) { printf("Port value '%s' is out of range\n", val); continue; } done = 1; } while (!done); sc_set(config, "listeners/tcp/@port", inp); /* KEY FILE */ printf("\nThe key file is the shared key information which is used to\n" "authenticate fencing requests. The contents of this file must\n" "be distributed to each physical host and virtual machine within\n" "a cluster.\n\n"); if (sc_get(config, "listeners/tcp/@key_file", val, sizeof(val)-1)) { strncpy(val, DEFAULT_KEY_FILE, sizeof(val)); } done = 0; do { text_input("Key File", val, inp, sizeof(inp)); if (!strcasecmp(inp, "none")) { break; } if (strlen(inp) > 0) { if (inp[0] != '/') { printf("Invalid key file: %s\n", inp); if (yesno("Use anyway", 1) == 1) done = 1; } else done = 1; } else printf("No key file given\n"); } while (!done); if (!strcasecmp(inp, "none")) { sc_set(config, "listeners/tcp/@key_file", NULL); } else { sc_set(config, "listeners/tcp/@key_file", inp); } return 0; } static int listener_config_serial(config_object_t *config) { char val[4096]; char inp[4096]; int done; printf("\n"); printf("The serial plugin allows fence_virtd to communicate with\n" "guests using serial or guest-forwarding VMChannel instead\n" "of using TCP/IP networking.\n\n"); printf("Special configuration of virtual machines is required. See\n" "fence_virt.conf(5) for more details.\n\n"); if (sc_get(config, "listeners/serial/@uri", val, sizeof(val)-1)) { strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val)); } text_input("Libvirt URI", val, inp, sizeof(inp)); printf("\nSetting a socket path prevents fence_virtd from taking\n" "hold of all Unix domain sockets created when the guest\n" "is started. A value like /var/run/cluster/fence might\n" "be a good value. Don't forget to create the directory!\n\n"); if (sc_get(config, "listeners/serial/@path", val, sizeof(val)-1)) { strncpy(val, "none", sizeof(val)); } text_input("Socket directory", val, inp, sizeof(inp)); if (!strcasecmp(inp, "none")) { sc_set(config, "listeners/serial/@path", NULL); } else { sc_set(config, "listeners/serial/@path", inp); } printf("\nThe serial plugin allows two types of guest to host\n" "configurations. One is via a serial port; the other is\n" "utilizing the newer VMChannel.\n\n"); if (sc_get(config, "listeners/serial/@mode", val, sizeof(val)-1)) { strncpy(val, "serial", sizeof(val)); } if (!strcasecmp(inp, "none")) { sc_set(config, "listeners/serial/@path", NULL); } else { sc_set(config, "listeners/serial/@path", inp); } done = 0; do { text_input("Mode (serial or vmchannel)", val, inp, sizeof(inp)); if (strcasecmp(inp, "serial") && strcasecmp(inp, "vmchannel")) { printf("Invalid mode: %s\n", inp); if (yesno("Use anyway", 1) == 1) done = 1; } else done = 1; } while (!done); sc_set(config, "listeners/serial/@mode", inp); return 0; } static int backend_configure(config_object_t *config) { char val[4096]; char inp[4096]; int done; printf("\n"); printf("Backend modules are responsible for routing requests to\n" "the appropriate hypervisor or management layer.\n\n"); /* Default backend plugin */ if (sc_get(config, "fence_virtd/@backend", val, sizeof(val))) { strncpy(val, "libvirt", sizeof(val)); } done = 0; do { text_input("Backend module", val, inp, sizeof(inp)); if (plugin_find_backend(inp) == NULL) { printf("No backend module named %s found!\n", inp); if (yesno("Use this value anyway", 0) == 1) done = 1; } else done = 1; } while (!done); sc_set(config, "fence_virtd/@backend", inp); if (!strcmp(inp, "libvirt")) { backend_config_libvirt(config); } else if (!strcmp(inp, "cpg")) { backend_config_cpg(config); } return 0; } static int listener_configure(config_object_t *config) { char val[4096]; char inp[4096]; int done; printf("\n"); printf("Listener modules are responsible for accepting requests\n" "from fencing clients.\n\n"); /* Default backend plugin */ if (sc_get(config, "fence_virtd/@listener", val, sizeof(val))) { strncpy(val, "multicast", sizeof(val)); } done = 0; do { text_input("Listener module", val, inp, sizeof(inp)); if (plugin_find_listener(inp) == NULL) { printf("No listener module named %s found!\n", inp); if (yesno("Use this value anyway", 0) == 1) done = 1; } else done = 1; } while (!done); sc_set(config, "fence_virtd/@listener", inp); if (!strcmp(inp, "multicast")) listener_config_multicast(config); else if (!strcmp(inp, "tcp")) listener_config_tcp(config); else if (!strcmp(inp, "serial")) listener_config_serial(config); else printf("Unable to configure unknown listner module '%s'\n", inp); return 0; } int do_configure(config_object_t *config, const char *config_file) { FILE *fp = NULL; char message[80]; char tmp_filename[4096]; int tmp_fd = -1; mode_t old_umask; if (sc_parse(config, config_file) != 0) { printf("Parsing of %s failed.\n", config_file); if (yesno("Start from scratch", 0) == 0) { return 1; } } plugin_path_configure(config); listener_configure(config); backend_configure(config); printf("\nConfiguration complete.\n\n"); printf("=== Begin Configuration ===\n"); sc_dump(config, stdout); printf("=== End Configuration ===\n"); snprintf(message, sizeof(message), "Replace %s with the above", config_file); if (yesno(message, 0) == 0) { return 1; } snprintf(tmp_filename, sizeof(tmp_filename), "%s.XXXXXX", config_file); old_umask = umask(077); tmp_fd = mkstemp(tmp_filename); umask(old_umask); if (tmp_fd < 0) { perror("fopen"); printf("Failed to write configuration file!\n"); return 1; } fp = fdopen(tmp_fd, "w+"); if (fp == NULL) goto out_fail; sc_dump(config, fp); if (rename(tmp_filename, config_file) < 0) { perror("rename"); goto out_fail; } fclose(fp); close(tmp_fd); return 0; out_fail: if (fp) fclose(fp); if (tmp_fd >= 0) close(tmp_fd); if (strlen(tmp_filename)) unlink(tmp_filename); printf("Failed to write configuration file!\n"); return 1; } fence-virt-1.0.0/server/cpg-virt.c000066400000000000000000000346011356743026700170000ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Ryan McCabe */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "virt.h" #include "xvm.h" #include "cpg.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #define NAME "cpg" #define CPG_VERSION "0.1" #define MAGIC 0x38e93fc2 struct cpg_info { int magic; config_object_t *config; int vp_count; virConnectPtr *vp; }; #define VALIDATE(arg) \ do {\ if (!arg || ((struct cpg_info *) arg)->magic != MAGIC) { \ errno = EINVAL;\ return -1; \ } \ } while(0) static struct cpg_info *cpg_virt_handle = NULL; static int use_uuid = 0; pthread_mutex_t local_vm_list_lock = PTHREAD_MUTEX_INITIALIZER; static virt_list_t *local_vm_list = NULL; pthread_mutex_t remote_vm_list_lock = PTHREAD_MUTEX_INITIALIZER; static virt_list_t *remote_vm_list = NULL; static void cpg_virt_init_libvirt(struct cpg_info *info); static int virt_list_update(struct cpg_info *info, virt_list_t **vl, int my_id) { virt_list_t *list = NULL; if (*vl) vl_free(*vl); list = vl_get(info->vp, info->vp_count, my_id); if (!list && (errno == EPIPE || errno == EINVAL)) { do { cpg_virt_init_libvirt(info); } while (info->vp_count == 0); list = vl_get(info->vp, info->vp_count, my_id); } *vl = list; if (!list) return -1; return 0; } static void store_domains(virt_list_t *vl) { int i; if (!vl) return; for (i = 0 ; i < vl->vm_count ; i++) { int ret; if (!strcmp(DOMAIN0NAME, vl->vm_states[i].v_name)) continue; ret = cpg_send_vm_state(&vl->vm_states[i]); if (ret < 0) { printf("Error storing VM state for %s|%s\n", vl->vm_states[i].v_name, vl->vm_states[i].v_uuid); } } } static void update_local_vms(struct cpg_info *info) { uint32_t my_id = 0; if (!info) return; cpg_get_ids(&my_id, NULL); virt_list_update(info, &local_vm_list, my_id); store_domains(local_vm_list); } static int do_off(struct cpg_info *info, const char *vm_name) { dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name); return vm_off(info->vp, info->vp_count, vm_name); } static int do_on(struct cpg_info *info, const char *vm_name) { dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name); return vm_on(info->vp, info->vp_count, vm_name); } static int do_reboot(struct cpg_info *info, const char *vm_name) { dbg_printf(5, "%s %s\n", __FUNCTION__, vm_name); return vm_reboot(info->vp, info->vp_count, vm_name); } static void cpg_join_cb(const struct cpg_address *join, size_t joinlen) { struct cpg_info *info = cpg_virt_handle; pthread_mutex_lock(&local_vm_list_lock); update_local_vms(info); pthread_mutex_unlock(&local_vm_list_lock); } static void cpg_leave_cb(const struct cpg_address *left, size_t leftlen) { struct cpg_info *info = cpg_virt_handle; int i; pthread_mutex_lock(&remote_vm_list_lock); for (i = 0 ; i < leftlen ; i++) { dbg_printf(2, "Removing VMs owned by nodeid %u\n", left[i].nodeid); vl_remove_by_owner(&remote_vm_list, left[i].nodeid); } pthread_mutex_unlock(&remote_vm_list_lock); pthread_mutex_lock(&local_vm_list_lock); update_local_vms(info); pthread_mutex_unlock(&local_vm_list_lock); } static void store_cb(void *data, size_t len, uint32_t nodeid, uint32_t seqno) { uint32_t my_id; virt_state_t *vs = (virt_state_t *) data; struct cpg_info *info = cpg_virt_handle; cpg_get_ids(&my_id, NULL); if (nodeid == my_id) return; pthread_mutex_lock(&local_vm_list_lock); if (!local_vm_list) update_local_vms(info); pthread_mutex_unlock(&local_vm_list_lock); pthread_mutex_lock(&remote_vm_list_lock); vl_update(&remote_vm_list, vs); pthread_mutex_unlock(&remote_vm_list_lock); } /* ** This function must a send reply from at least one node, otherwise ** the requesting fence_virtd will block forever in wait_cpt_reply. */ static void do_real_work(void *data, size_t len, uint32_t nodeid, uint32_t seqno) { struct cpg_info *info = cpg_virt_handle; struct cpg_fence_req *req = data; struct cpg_fence_req reply; int reply_code = -1; virt_state_t *vs = NULL; int cur_state; uint32_t cur_owner = 0; int local = 0; uint32_t my_id, high_id; dbg_printf(2, "Request %d for VM %s\n", req->request, req->vm_name); if (cpg_get_ids(&my_id, &high_id) == -1) { syslog(LOG_WARNING, "Unable to get CPG IDs"); printf("Should never happen: Can't get CPG node ids - can't proceed\n"); return; } memcpy(&reply, req, sizeof(reply)); pthread_mutex_lock(&local_vm_list_lock); update_local_vms(info); if (strlen(req->vm_name)) { if (use_uuid) vs = vl_find_uuid(local_vm_list, req->vm_name); else vs = vl_find_name(local_vm_list, req->vm_name); if (vs) { local = 1; cur_owner = vs->v_state.s_owner; cur_state = vs->v_state.s_state; dbg_printf(2, "Found VM %s locally state %d\n", req->vm_name, cur_state); } } pthread_mutex_unlock(&local_vm_list_lock); if (vs == NULL) { pthread_mutex_lock(&remote_vm_list_lock); if (strlen(req->vm_name)) { if (use_uuid) vs = vl_find_uuid(remote_vm_list, req->vm_name); else vs = vl_find_name(remote_vm_list, req->vm_name); if (vs) { cur_owner = vs->v_state.s_owner; cur_state = vs->v_state.s_state; dbg_printf(2, "Found VM %s remotely on %u state %d\n", req->vm_name, cur_owner, cur_state); } } pthread_mutex_unlock(&remote_vm_list_lock); } if (!vs) { /* ** We know about all domains on all nodes in the CPG group. ** If we didn't find it, and we're high ID, act on the request. ** We can safely assume the VM is OFF because it wasn't found ** on any current members of the CPG group. */ if (my_id == high_id) { if (req->request == FENCE_STATUS) reply_code = RESP_OFF; else if (req->request == FENCE_OFF || req->request == FENCE_REBOOT) reply_code = RESP_SUCCESS; else reply_code = 1; dbg_printf(2, "Acting on request %d for unknown domain %s -> %d\n", req->request, req->vm_name, reply_code); goto out; } dbg_printf(2, "Not acting on request %d for unknown domain %s\n", req->request, req->vm_name); return; } if (local) { if (req->request == FENCE_STATUS) { /* We already have the status */ if (cur_state == VIR_DOMAIN_SHUTOFF) reply_code = RESP_OFF; else reply_code = RESP_SUCCESS; } else if (req->request == FENCE_OFF) { reply_code = do_off(info, req->vm_name); } else if (req->request == FENCE_ON) { reply_code = do_on(info, req->vm_name); } else if (req->request == FENCE_REBOOT) { reply_code = do_reboot(info, req->vm_name); } else { dbg_printf(2, "Not explicitly handling request type %d for %s\n", req->request, req->vm_name); reply_code = 0; } goto out; } /* ** This is a request for a non-local domain that exists on a ** current CPG group member, so that member will see the request ** and act on it. We don't need to do anything. */ dbg_printf(2, "Nothing to do for non-local domain %s seq %d owner %u\n", req->vm_name, seqno, cur_owner); return; out: dbg_printf(2, "[%s] sending reply code seq %d -> %d\n", req->vm_name, seqno, reply_code); reply.response = reply_code; if (cpg_send_reply(&reply, sizeof(reply), nodeid, seqno) < 0) { dbg_printf(2, "cpg_send_reply failed for %s [%d %d]: %s\n", req->vm_name, nodeid, seqno, strerror(errno)); } } static int do_request(const char *vm_name, int request, uint32_t seqno) { struct cpg_fence_req freq, *frp; size_t retlen; uint32_t seq; int ret; memset(&freq, 0, sizeof(freq)); if (!vm_name) { dbg_printf(1, "No VM name\n"); return 1; } if (strlen(vm_name) >= sizeof(freq.vm_name)) { dbg_printf(1, "VM name %s too long\n", vm_name); return 1; } strcpy(freq.vm_name, vm_name); freq.request = request; freq.seqno = seqno; if (cpg_send_req(&freq, sizeof(freq), &seq) != 0) { dbg_printf(1, "Failed to send request %d for VM %s\n", freq.request, vm_name); return 1; } dbg_printf(2, "Sent request %d for VM %s got seqno %d\n", request, vm_name, seq); if (cpg_wait_reply((void *) &frp, &retlen, seq) != 0) { dbg_printf(1, "Failed to receive reply seq %d for %s\n", seq, vm_name); return 1; } dbg_printf(2, "Received reply [%d] seq %d for %s\n", frp->response, seq, vm_name); ret = frp->response; free(frp); return ret; } static int cpg_virt_null(const char *vm_name, void *priv) { VALIDATE(priv); printf("[cpg-virt] Null operation on %s\n", vm_name); return 1; } static int cpg_virt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[cpg-virt] OFF operation on %s seq %d\n", vm_name, seqno); return do_request(vm_name, FENCE_OFF, seqno); } static int cpg_virt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[cpg-virt] ON operation on %s seq %d\n", vm_name, seqno); return do_request(vm_name, FENCE_ON, seqno); } static int cpg_virt_devstatus(void *priv) { printf("[cpg-virt] Device status\n"); VALIDATE(priv); return 0; } static int cpg_virt_status(const char *vm_name, void *priv) { VALIDATE(priv); printf("[cpg-virt] STATUS operation on %s\n", vm_name); return do_request(vm_name, FENCE_STATUS, 0); } static int cpg_virt_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[cpg-virt] REBOOT operation on %s seq %d\n", vm_name, seqno); return do_request(vm_name, FENCE_REBOOT, 0); } static int cpg_virt_hostlist(hostlist_callback callback, void *arg, void *priv) { struct cpg_info *info = (struct cpg_info *) priv; int i; VALIDATE(priv); printf("[cpg-virt] HOSTLIST operation\n"); pthread_mutex_lock(&local_vm_list_lock); update_local_vms(info); for (i = 0 ; i < local_vm_list->vm_count ; i++) { callback(local_vm_list->vm_states[i].v_name, local_vm_list->vm_states[i].v_uuid, local_vm_list->vm_states[i].v_state.s_state, arg); } pthread_mutex_unlock(&local_vm_list_lock); return 1; } static void cpg_virt_init_libvirt(struct cpg_info *info) { config_object_t *config = info->config; int i = 0; if (info->vp) { dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n"); for (i = 0 ; i < info->vp_count ; i++) virConnectClose(info->vp[i]); free(info->vp); info->vp = NULL; } info->vp_count = 0; do { virConnectPtr vp; virConnectPtr *vpl = NULL; char conf_attr[256]; char value[1024]; char *uri; if (i != 0) { snprintf(conf_attr, sizeof(conf_attr), "backends/cpg/@uri%d", i); } else snprintf(conf_attr, sizeof(conf_attr), "backends/cpg/@uri"); ++i; if (sc_get(config, conf_attr, value, sizeof(value)) != 0) break; uri = value; vp = virConnectOpen(uri); if (!vp) { dbg_printf(1, "[cpg-virt:INIT] Failed to connect to URI: %s\n", uri); continue; } vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1)); if (!vpl) { dbg_printf(1, "[cpg-virt:INIT] Out of memory allocating URI: %s\n", uri); virConnectClose(vp); continue; } info->vp = vpl; info->vp[info->vp_count++] = vp; if (i > 1) dbg_printf(1, "[cpg-virt:INIT] Added URI%d %s\n", i - 1, uri); else dbg_printf(1, "[cpg_virt:INIT] Added URI %s\n", uri); } while (1); } static int cpg_virt_init(backend_context_t *c, config_object_t *config) { char value[1024]; struct cpg_info *info = NULL; int ret; ret = cpg_start(PACKAGE_NAME, do_real_work, store_cb, cpg_join_cb, cpg_leave_cb); if (ret < 0) return -1; info = calloc(1, sizeof(*info)); if (!info) return -1; info->magic = MAGIC; info->config = config; #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0) dset(atoi(value)); #endif cpg_virt_init_libvirt(info); /* Naming scheme is no longer a top-level config option. * However, we retain it here for configuration compatibility with * versions 0.1.3 and previous. */ if (sc_get(config, "fence_virtd/@name_mode", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for name_mode\n", value); if (!strcasecmp(value, "uuid")) { use_uuid = 1; } else if (!strcasecmp(value, "name")) { use_uuid = 0; } else { dbg_printf(1, "Unsupported name_mode: %s\n", value); } } if (sc_get(config, "backends/cpg/@name_mode", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for name_mode\n", value); if (!strcasecmp(value, "uuid")) { use_uuid = 1; } else if (!strcasecmp(value, "name")) { use_uuid = 0; } else { dbg_printf(1, "Unsupported name_mode: %s\n", value); } } if (info->vp_count < 1) { dbg_printf(1, "[cpg_virt:INIT] Could not connect to any hypervisors\n"); cpg_stop(); free(info); return -1; } pthread_mutex_lock(&local_vm_list_lock); update_local_vms(info); pthread_mutex_unlock(&local_vm_list_lock); *c = (void *) info; cpg_virt_handle = info; return 0; } static int cpg_virt_shutdown(backend_context_t c) { struct cpg_info *info = (struct cpg_info *)c; int i = 0; int ret = 0; VALIDATE(info); info->magic = 0; cpg_stop(); for (i = 0 ; i < info->vp_count ; i++) { if (virConnectClose(info->vp[i]) < 0) ret = -errno; } free(info->vp); free(info); return ret; } static fence_callbacks_t cpg_callbacks = { .null = cpg_virt_null, .off = cpg_virt_off, .on = cpg_virt_on, .reboot = cpg_virt_reboot, .status = cpg_virt_status, .devstatus = cpg_virt_devstatus, .hostlist = cpg_virt_hostlist }; static backend_plugin_t cpg_virt_plugin = { .name = NAME, .version = CPG_VERSION, .callbacks = &cpg_callbacks, .init = cpg_virt_init, .cleanup = cpg_virt_shutdown, }; #ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &cpg_virt_plugin; } #else static void __attribute__((constructor)) cpg_register_plugin(void) { plugin_reg_backend(&cpg_virt_plugin); } #endif fence-virt-1.0.0/server/cpg.c000066400000000000000000000164411356743026700160200ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "virt.h" #include "cpg.h" #define NODE_ID_NONE ((uint32_t) -1) struct msg_queue_node { list_head(); uint32_t seqno; #define STATE_CLEAR 0 #define STATE_MESSAGE 1 uint32_t state; void *msg; size_t msglen; }; struct wire_msg { #define TYPE_REQUEST 0 #define TYPE_REPLY 1 #define TYPE_STORE_VM 2 uint32_t type; uint32_t seqno; uint32_t target; uint32_t pad; char data[0]; }; static uint32_t seqnum = 0; static struct msg_queue_node *pending = NULL; static cpg_handle_t cpg_handle; static struct cpg_name gname; static pthread_mutex_t cpg_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cpg_cond = PTHREAD_COND_INITIALIZER; static pthread_t cpg_thread = 0; static pthread_mutex_t cpg_ids_mutex = PTHREAD_MUTEX_INITIALIZER; static uint32_t my_node_id = NODE_ID_NONE; static uint32_t high_id_from_callback = NODE_ID_NONE; static request_callback_fn req_callback_fn; static request_callback_fn store_callback_fn; static confchange_callback_fn conf_leave_fn; static confchange_callback_fn conf_join_fn; int cpg_get_ids(uint32_t *my_id, uint32_t *high_id) { if (!my_id && !high_id) return -1; pthread_mutex_lock(&cpg_ids_mutex); if (my_id) *my_id = my_node_id; if (high_id) *high_id = high_id_from_callback; pthread_mutex_unlock(&cpg_ids_mutex); return 0; } void cpg_deliver_func(cpg_handle_t h, const struct cpg_name *group_name, uint32_t nodeid, uint32_t pid, void *msg, size_t msglen) { struct msg_queue_node *n; struct wire_msg *m = msg; int x, found; pthread_mutex_lock(&cpg_mutex); if (m->type == TYPE_REPLY) { /* Reply to a request we sent */ found = 0; list_for(&pending, n, x) { if (m->seqno != n->seqno) continue; if (m->target != my_node_id) continue; found = 1; break; } if (!found) goto out_unlock; /* Copy our message in to a buffer */ n->msglen = msglen - sizeof(*m); if (!n->msglen) { /* XXX do what? */ } n->msg = malloc(n->msglen); if (!n->msg) { goto out_unlock; } n->state = STATE_MESSAGE; memcpy(n->msg, (char *)msg + sizeof(*m), n->msglen); list_remove(&pending, n); list_insert(&pending, n); dbg_printf(2, "Seqnum %d replied; removing from list\n", n->seqno); pthread_cond_broadcast(&cpg_cond); goto out_unlock; } pthread_mutex_unlock(&cpg_mutex); if (m->type == TYPE_REQUEST) { req_callback_fn(&m->data, msglen - sizeof(*m), nodeid, m->seqno); } if (m->type == TYPE_STORE_VM) { store_callback_fn(&m->data, msglen - sizeof(*m), nodeid, m->seqno); } return; out_unlock: pthread_mutex_unlock(&cpg_mutex); } void cpg_config_change(cpg_handle_t h, const struct cpg_name *group_name, const struct cpg_address *members, size_t memberlen, const struct cpg_address *left, size_t leftlen, const struct cpg_address *join, size_t joinlen) { int x; int high; pthread_mutex_lock(&cpg_ids_mutex); high = my_node_id; for (x = 0; x < memberlen; x++) { if (members[x].nodeid > high) high = members[x].nodeid; } high_id_from_callback = high; pthread_mutex_unlock(&cpg_ids_mutex); if (joinlen > 0) conf_join_fn(join, joinlen); if (leftlen > 0) conf_leave_fn(left, leftlen); } static cpg_callbacks_t my_callbacks = { .cpg_deliver_fn = cpg_deliver_func, .cpg_confchg_fn = cpg_config_change }; int cpg_send_req(void *data, size_t len, uint32_t *seqno) { struct iovec iov; struct msg_queue_node *n; struct wire_msg *m; size_t msgsz = sizeof(*m) + len; int ret; n = malloc(sizeof(*n)); if (!n) return -1; m = malloc(msgsz); if (!m) { free(n); return -1; } /* only incremented on send */ n->state = STATE_CLEAR; n->msg = NULL; n->msglen = 0; pthread_mutex_lock(&cpg_mutex); list_insert(&pending, n); n->seqno = ++seqnum; m->seqno = seqnum; *seqno = seqnum; pthread_mutex_unlock(&cpg_mutex); m->type = TYPE_REQUEST; /* XXX swab? */ m->target = NODE_ID_NONE; memcpy(&m->data, data, len); iov.iov_base = m; iov.iov_len = msgsz; ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1); free(m); if (ret == CS_OK) return 0; return -1; } int cpg_send_vm_state(virt_state_t *vs) { struct iovec iov; struct msg_queue_node *n; struct wire_msg *m; size_t msgsz = sizeof(*m) + sizeof(*vs); int ret; n = calloc(1, (sizeof(*n))); if (!n) return -1; m = calloc(1, msgsz); if (!m) { free(n); return -1; } n->state = STATE_MESSAGE; n->msg = NULL; n->msglen = 0; pthread_mutex_lock(&cpg_mutex); list_insert(&pending, n); pthread_mutex_unlock(&cpg_mutex); m->type = TYPE_STORE_VM; m->target = NODE_ID_NONE; memcpy(&m->data, vs, sizeof(*vs)); iov.iov_base = m; iov.iov_len = msgsz; ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1); free(m); if (ret == CS_OK) return 0; return -1; } int cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno) { struct iovec iov; struct wire_msg *m; size_t msgsz = sizeof(*m) + len; int ret; m = malloc(msgsz); if (!m) return -1; /* only incremented on send */ m->seqno = seqno; m->type = TYPE_REPLY; /* XXX swab? */ m->target = nodeid; memcpy(&m->data, data, len); iov.iov_base = m; iov.iov_len = msgsz; ret = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, &iov, 1); free(m); if (ret == CS_OK) return 0; return -1; } int cpg_wait_reply(void **data, size_t *len, uint32_t seqno) { struct msg_queue_node *n; int x, found = 0; while (!found) { found = 0; pthread_mutex_lock(&cpg_mutex); pthread_cond_wait(&cpg_cond, &cpg_mutex); list_for(&pending, n, x) { if (n->seqno != seqno) continue; if (n->state != STATE_MESSAGE) continue; found = 1; goto out; } pthread_mutex_unlock(&cpg_mutex); } out: list_remove(&pending, n); pthread_mutex_unlock(&cpg_mutex); *data = n->msg; *len = n->msglen; free(n); return 0; } static void * cpg_dispatch_thread(void *arg) { cpg_dispatch(cpg_handle, CS_DISPATCH_BLOCKING); return NULL; } int cpg_start( const char *name, request_callback_fn req_cb_fn, request_callback_fn store_cb_fn, confchange_callback_fn join_fn, confchange_callback_fn leave_fn) { cpg_handle_t h; int ret; errno = EINVAL; if (!name) return -1; ret = snprintf(gname.value, sizeof(gname.value), "%s", name); if (ret <= 0) return -1; if (ret >= sizeof(gname.value)) { errno = ENAMETOOLONG; return -1; } gname.length = ret; memset(&h, 0, sizeof(h)); if (cpg_initialize(&h, &my_callbacks) != CS_OK) { perror("cpg_initialize"); return -1; } if (cpg_join(h, &gname) != CS_OK) { perror("cpg_join"); return -1; } cpg_local_get(h, &my_node_id); dbg_printf(2, "My CPG nodeid is %d\n", my_node_id); pthread_mutex_lock(&cpg_mutex); pthread_create(&cpg_thread, NULL, cpg_dispatch_thread, NULL); memcpy(&cpg_handle, &h, sizeof(h)); req_callback_fn = req_cb_fn; store_callback_fn = store_cb_fn; conf_join_fn = join_fn; conf_leave_fn = leave_fn; pthread_mutex_unlock(&cpg_mutex); return 0; } int cpg_stop(void) { pthread_cancel(cpg_thread); pthread_join(cpg_thread, NULL); cpg_leave(cpg_handle, &gname); cpg_finalize(cpg_handle); return 0; } fence-virt-1.0.0/server/cpg.h000066400000000000000000000014651356743026700160250ustar00rootroot00000000000000#ifndef __FENCE_VIRTD_CPG_H #define __FENCE_VIRTD_CPG_H struct cpg_fence_req { char vm_name[128]; int request; uint32_t seqno; uint32_t response; }; typedef void (*request_callback_fn)(void *data, size_t len, uint32_t nodeid, uint32_t seqno); typedef void (*confchange_callback_fn)(const struct cpg_address *m, size_t len); int cpg_start( const char *name, request_callback_fn func, request_callback_fn store_func, confchange_callback_fn join, confchange_callback_fn leave); int cpg_get_ids(uint32_t *me, uint32_t *high); int cpg_stop(void); int cpg_send_req(void *data, size_t len, uint32_t *seqno); int cpg_wait_reply(void **data, size_t *len, uint32_t seqno); int cpg_send_reply(void *data, size_t len, uint32_t nodeid, uint32_t seqno); int cpg_send_vm_state(virt_state_t *vs); #endif fence-virt-1.0.0/server/daemon_init.c000066400000000000000000000104731356743026700175340ustar00rootroot00000000000000/** @file * daemon_init function, does sanity checks and calls daemon(). * * Author: Jeff Moyer */ /* * TODO: Clean this up so that only one function constructs the * pidfile /var/run/loggerd.PID, and perhaps only one function * forms the /proc/PID/ path. * * Also need to add file locking for the pid file. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This should ultimately go in a header file. */ void daemon_init(const char *prog, const char *pid_file, int nofork); void daemon_cleanup(void); int check_process_running(const char *cmd, const char *pid_file, pid_t * pid); /* * Local prototypes. */ static void update_pidfile(const char *filename); static int setup_sigmask(void); static char pid_filename[PATH_MAX]; static int check_pid_valid(pid_t pid, const char *prog) { FILE *fp; DIR *dir; char filename[PATH_MAX]; char dirpath[PATH_MAX]; char proc_cmdline[64]; /* yank this from kernel somewhere */ char *s = NULL; memset(filename, 0, PATH_MAX); memset(dirpath, 0, PATH_MAX); snprintf(dirpath, sizeof (dirpath), "/proc/%d", pid); if ((dir = opendir(dirpath)) == NULL) { return 0; /* Pid has gone away. */ } closedir(dir); /* * proc-pid directory exists. Now check to see if this * PID corresponds to the daemon we want to start. */ snprintf(filename, sizeof (filename), "/proc/%d/cmdline", pid); fp = fopen(filename, "r"); if (fp == NULL) { perror("check_pid_valid"); return 0; /* Who cares.... Let's boogy on. */ } if (!fgets(proc_cmdline, sizeof (proc_cmdline) - 1, fp)) { /* * Okay, we've seen processes keep a reference to a * /proc/PID/stat file and not let go. Then when * you try to read /proc/PID/cmline, you get either * \000 or -1. In either case, we can safely assume * the process has gone away. */ fclose(fp); return 0; } fclose(fp); s = &(proc_cmdline[strlen(proc_cmdline)]); if (*s == '\n') *s = 0; /* * Check to see if this is the same executable. */ if (strstr(proc_cmdline, prog) == NULL) { return 0; } else { return 1; } } int check_process_running(const char *cmd, const char *filename, pid_t * pid) { pid_t oldpid; FILE *fp; int ret; struct stat st; *pid = -1; /* * Now see if there is a pidfile associated with this cmd in /var/run */ fp = NULL; ret = stat(filename, &st); if ((ret < 0) || (!st.st_size)) return 0; /* * Read the pid from the file. */ fp = fopen(filename, "r"); if (fp == NULL) { /* error */ return 0; } ret = fscanf(fp, "%d\n", &oldpid); fclose(fp); if ((ret == EOF) || (ret != 1)) return 0; if (check_pid_valid(oldpid, cmd)) { *pid = oldpid; return 1; } return 0; } static void update_pidfile(const char *filename) { FILE *fp = NULL; strncpy(pid_filename, filename, PATH_MAX - 1); fp = fopen(pid_filename, "w"); if (fp == NULL) { syslog(LOG_ERR, "daemon_init: Unable to create pidfile %s: %s\n", filename, strerror(errno)); exit(1); } fprintf(fp, "%d", getpid()); fclose(fp); } static int setup_sigmask(void) { sigset_t set; sigfillset(&set); /* * Dont't block signals which would cause us to dump core. */ sigdelset(&set, SIGQUIT); sigdelset(&set, SIGILL); sigdelset(&set, SIGTRAP); sigdelset(&set, SIGABRT); sigdelset(&set, SIGFPE); sigdelset(&set, SIGSEGV); sigdelset(&set, SIGBUS); /* * Don't block SIGTERM or SIGCHLD */ sigdelset(&set, SIGTERM); sigdelset(&set, SIGINT); sigdelset(&set, SIGQUIT); sigdelset(&set, SIGCHLD); return (sigprocmask(SIG_BLOCK, &set, NULL)); } void daemon_init(const char *prog, const char *pid_file, int nofork) { pid_t pid; if (check_process_running(prog, pid_file, &pid) && (pid != getpid())) { syslog(LOG_ERR, "daemon_init: Process \"%s\" already running.\n", prog); exit(1); } if (setup_sigmask() < 0) { syslog(LOG_ERR, "daemon_init: Unable to set signal mask.\n"); exit(1); } if (!nofork && daemon(0, 0)) { syslog(LOG_ERR, "daemon_init: Unable to daemonize.\n"); exit(1); } update_pidfile(pid_file); } void daemon_cleanup(void) { if (strlen(pid_filename)) unlink(pid_filename); } fence-virt-1.0.0/server/history.c000066400000000000000000000037311356743026700167460ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include "history.h" history_info_t * history_init(history_compare_fn func, time_t expiration, size_t element_size) { history_info_t *hist; errno = EINVAL; if (!func || !expiration || !element_size) return NULL; hist = malloc(sizeof(*hist)); if (!hist) return NULL; memset(hist, 0, sizeof(*hist)); hist->timeout = expiration; hist->element_size = element_size; hist->compare_func = func; return hist; } /* * Purge our history when the entries time out. * * Returns 1 if a matching history node was found, or 0 * if not. */ int history_check(history_info_t *hinfo, void *stuff) { history_node *entry = NULL; time_t now; int x; if (!hinfo) return 0; /* XXX */ if (!hinfo->hist) return 0; now = time(NULL); loop_again: list_for((&hinfo->hist), entry, x) { if (entry->when < (now - hinfo->timeout)) { list_remove((&hinfo->hist), entry); free(entry->data); free(entry); goto loop_again; } if (hinfo->compare_func(entry->data, stuff)) { return 1; } } return 0; } int history_record(history_info_t *hinfo, void *data) { history_node *entry = NULL; errno = EINVAL; if (!data || !hinfo) return -1; if (history_check(hinfo, data) == 1) { errno = EEXIST; return -1; } entry = malloc(sizeof(*entry)); if (!entry) { return -1; } memset(entry, 0, sizeof(*entry)); entry->data = malloc(hinfo->element_size); if (!entry->data) { free(entry); errno = ENOMEM; return -1; } memcpy(entry->data, data, hinfo->element_size); entry->when = time(NULL); list_insert((&hinfo->hist), entry); return 0; } int history_wipe(history_info_t *hinfo) { history_node *entry = NULL; if (!hinfo) return -1; while (hinfo->hist) { entry = hinfo->hist; list_remove((&hinfo->hist), entry); free(entry->data); free(entry); } /* User must free(hinfo); */ return 0; } fence-virt-1.0.0/server/libvirt-qmf.cpp000066400000000000000000000241201356743026700200340ustar00rootroot00000000000000// // Copyright Red Hat, Inc. 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, 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; see the file COPYING. If not, write to the // Free Software Foundation, Inc., 675 Mass Ave, Cambridge, // MA 02139, USA. // // Author: Lon Hohberger // #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uuid-test.h" #include "xvm.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #define NAME "libvirt-qmf" #define VERSION "0.2" #define MAGIC 0x1e01017a struct lq_info { int magic; int port; char *host; char *username; char *service; int use_gssapi; }; #define VALIDATE(arg) \ do {\ if (!arg || ((struct lq_info *)arg)->magic != MAGIC) { \ errno = EINVAL;\ return -1; \ } \ } while(0) static qmf::ConsoleSession lq_open_session(struct lq_info *info) { qmf::ConsoleSession session; std::stringstream url; url << info->host << ":" << info->port; qpid::types::Variant::Map options; if (info->username) { options["username"] = info->username; } if (info->service) { options["sasl-service"] = info->service; } if (info->use_gssapi) { options["sasl-mechanism"] = "GSSAPI"; } try { qpid::messaging::Connection connection(url.str(), options); connection.open(); if (!connection.isOpen()) { std::cout << "Error connecting." << std::endl; } else { session = qmf::ConsoleSession(connection); std::stringstream filter; filter << "[or, " "[eq, _product, [quote, 'libvirt-qmf']], " "[eq, _product, [quote, 'libvirt-qpid']]" "]"; session.setAgentFilter(filter.str()); session.open(); } } catch (qpid::messaging::TransportFailure ex) { std::cout << "Error establishing session: " << ex.what() << std::endl; } return session; } static qmf::ConsoleEvent queryDomain(qmf::Agent& agent) { std::string query; if (agent.getProduct() == "libvirt-qmf") { query = "{class: Domain, package: 'org.libvirt'}"; } else { query = "{class: domain, package: 'com.redhat.libvirt'}"; } return agent.query(query); } int do_lq_request(struct lq_info *info, const char *vm_name, const char *action) { std::string vm_state; const char *property = "name"; if (is_uuid(vm_name) == 1) { property = "uuid"; } qmf::ConsoleSession session(lq_open_session(info)); if (!session.isValid()) { std::cout << "Invalid session." << std::endl; return 1; } qmf::Agent agent; qmf::Data domain; int result; unsigned tries = 0; bool found = false; while (++tries < 10 && !found) { sleep(1); uint32_t numAgents = session.getAgentCount(); for (unsigned a = 0; !found && a < numAgents; a++) { agent = session.getAgent(a); qmf::ConsoleEvent event(queryDomain(agent)); uint32_t numDomains = event.getDataCount(); for (unsigned d = 0; !found && d < numDomains; d++) { domain = event.getData(d); qpid::types::Variant prop; try { prop = domain.getProperty(property); } catch (qmf::KeyNotFound e) { std::cout << e.what() << " - skipping" << std::endl; continue; } if (prop.asString() != vm_name) { continue; } found = true; } } } if (!found) { result = 1; goto out; } vm_state = domain.getProperty("state").asString(); std::cout << vm_name << " " << vm_state << std::endl; int r; if (vm_state == "running" || vm_state == "idle" || vm_state == "paused" || vm_state == "no state") { r = RESP_OFF; } else { r = 0; } if (strcasecmp(action, "state") == 0) { result = r; goto out; } result = 1; if (!r && strcasecmp(action, "destroy") == 0) { std::cout << "Domain is inactive; nothing to do" << std::endl; result = 0; goto out; } if (r && strcasecmp(action, "create") == 0) { std::cout << "Domain is active; nothing to do" << std::endl; result = 0; goto out; } { qmf::ConsoleEvent response; response = agent.callMethod(action, qpid::types::Variant::Map(), domain.getAddr()); if (response.getType() == qmf::CONSOLE_EXCEPTION) { std::string errorText; if (response.getDataCount()) { qmf::Data responseData(response.getData(0)); qpid::types::Variant code(responseData.getProperty("error_code")); if (code.getType() == qpid::types::VAR_INT32) { result = responseData.getProperty("error_code").asInt32(); } else { result = 7; // Exception } qpid::types::Variant text(responseData.getProperty("error_text")); if (text.getType() != qpid::types::VAR_VOID) { errorText = text.asString(); } } else { result = 7; // Exception } std::cout << "Response: " << result; if (errorText.length()) { std::cout << " (" << errorText << ")"; } std::cout << std::endl; } else { // Success result = 0; } } out: session.close(); return result; } static int lq_null(const char *vm_name, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] NULL operation on %s\n", vm_name); return 1; } static int lq_off(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] OFF operation on %s\n", vm_name); return do_lq_request((lq_info *)priv, vm_name, "destroy"); return 1; } static int lq_on(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] ON operation on %s\n", vm_name); return do_lq_request((lq_info *)priv, vm_name, "create"); } static int lq_devstatus(void *priv) { VALIDATE(priv); printf("[libvirt-qmf] Device status\n"); return 0; } static int lq_status(const char *vm_name, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] STATUS operation on %s\n", vm_name); return do_lq_request((lq_info *)priv, vm_name, "state"); } static int lq_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] REBOOT operation on %s\n", vm_name); if (lq_off(vm_name, src, seqno, priv) != 0) return 1; sleep(1); lq_on(vm_name, src, seqno, priv); return 0; } static int lq_hostlist(hostlist_callback callback, void *arg, void *priv) { VALIDATE(priv); printf("[libvirt-qmf] HOSTLIST operation\n"); qmf::ConsoleSession session(lq_open_session((struct lq_info *)priv)); if (!session.isValid()) { return 1; } unsigned tries = 0; qmf::ConsoleEvent event; uint32_t numDomains = 0; while (++tries < 10 && !numDomains) { sleep(1); uint32_t numAgents = session.getAgentCount(); for (unsigned a = 0; a < numAgents; a++) { qmf::Agent agent(session.getAgent(a)); event = queryDomain(agent); numDomains = event.getDataCount(); if (numDomains >= 1) { break; } } } for (unsigned d = 0; d < numDomains; d++) { qmf::Data domain = event.getData(d); std::string vm_name, vm_uuid, vm_state_str; try { vm_name = domain.getProperty("name").asString(); vm_uuid = domain.getProperty("uuid").asString(); vm_state_str = domain.getProperty("state").asString(); } catch (qmf::KeyNotFound e) { std::cout << e.what() << " - skipping" << std::endl; continue; } int vm_state; if (!strcasecmp(vm_state_str.c_str(), "shutoff")) { vm_state = 0; } else { vm_state = 1; } callback(vm_name.c_str(), vm_uuid.c_str(), vm_state, arg); } session.close(); return 0; } static int lq_init(backend_context_t *c, config_object_t *config) { char value[256]; struct lq_info *info = NULL; info = (lq_info *)malloc(sizeof(*info)); if (!info) return -1; memset(info, 0, sizeof(*info)); info->port = 5672; /* Actually match default qpid port */ if(sc_get(config, "backends/libvirt-qmf/@host", value, sizeof(value))==0){ printf("\n\nHOST = %s\n\n",value); info->host = strdup(value); if (!info->host) { goto out_fail; } } else { info->host = strdup("127.0.0.1"); } if(sc_get(config, "backends/libvirt-qmf/@port", value, sizeof(value)-1)==0){ printf("\n\nPORT = %d\n\n",atoi(value)); info->port = atoi(value); } if(sc_get(config, "backends/libvirt-qmf/@username", value, sizeof(value))==0){ printf("\n\nUSERNAME = %s\n\n",value); info->username = strdup(value); if (!info->username) { goto out_fail; } } if(sc_get(config, "backends/libvirt-qmf/@service", value, sizeof(value))==0){ printf("\n\nSERVICE = %s\n\n",value); info->service = strdup(value); if (!info->service) { goto out_fail; } } if(sc_get(config, "backends/libvirt-qmf/@gssapi", value, sizeof(value)-1)==0){ printf("\n\nGSSAPI = %d\n\n",atoi(value)); if (atoi(value) > 0) { info->use_gssapi = 1; } } info->magic = MAGIC; *c = (void *)info; return 0; out_fail: free(info->service); free(info->username); free(info->host); free(info); return -1; } static int lq_shutdown(backend_context_t c) { struct lq_info *info = (struct lq_info *)c; VALIDATE(info); info->magic = 0; free(info->service); free(info->username); free(info->host); free(info); return 0; } static fence_callbacks_t lq_callbacks = { lq_null, lq_off, lq_on, lq_reboot, lq_status, lq_devstatus, lq_hostlist }; static backend_plugin_t lq_plugin = { NAME, VERSION, &lq_callbacks, lq_init, lq_shutdown }; #ifdef _MODULE extern "C" double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } extern "C" const backend_plugin_t * BACKEND_INFO_SYM(void) { return &lq_plugin; } #else static void __attribute__((constructor)) lq_register_plugin(void) { plugin_reg_backend(&lq_plugin); } #endif fence-virt-1.0.0/server/libvirt.c000066400000000000000000000170371356743026700167240ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #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 /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "mcast.h" #include "tcp.h" #include "virt.h" #include "debug.h" #include "uuid-test.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #define NAME "libvirt" #define LIBVIRT_VERSION "0.3" #define MAGIC 0x1e19317a struct libvirt_info { int magic; config_object_t *config; int vp_count; virConnectPtr *vp; }; #define VALIDATE(arg) \ do {\ if (!arg || ((struct libvirt_info *)arg)->magic != MAGIC) { \ errno = EINVAL;\ return -1; \ } \ } while(0) static void libvirt_init_libvirt_conf(struct libvirt_info *info) { config_object_t *config = info->config; int i = 0; if (info->vp) { dbg_printf(2, "Lost libvirtd connection. Reinitializing.\n"); for (i = 0 ; i < info->vp_count ; i++) virConnectClose(info->vp[i]); free(info->vp); info->vp = NULL; } info->vp_count = 0; do { virConnectPtr vp; virConnectPtr *vpl = NULL; char conf_attr[256]; char value[1024]; char *uri; if (i != 0) { snprintf(conf_attr, sizeof(conf_attr), "backends/libvirt/@uri%d", i); } else snprintf(conf_attr, sizeof(conf_attr), "backends/libvirt/@uri"); ++i; if (sc_get(config, conf_attr, value, sizeof(value)) != 0) break; uri = value; vp = virConnectOpen(uri); if (!vp) { dbg_printf(1, "[libvirt:INIT] Failed to connect to URI: %s\n", uri); continue; } vpl = realloc(info->vp, sizeof(*info->vp) * (info->vp_count + 1)); if (!vpl) { dbg_printf(1, "[libvirt:INIT] Out of memory allocating URI: %s\n", uri); virConnectClose(vp); continue; } info->vp = vpl; info->vp[info->vp_count++] = vp; if (i > 1) dbg_printf(1, "[libvirt:INIT] Added URI%d %s\n", i - 1, uri); else dbg_printf(1, "[libvirt:INIT] Added URI %s\n", uri); } while (1); } static int libvirt_bad_connections(struct libvirt_info *info) { int bad = 0; int i; for (i = 0 ; i < info->vp_count ; i++) { /* ** Send a dummy command to trigger an error if libvirtd ** died or restarted */ virConnectNumOfDomains(info->vp[i]); if (!virConnectIsAlive(info->vp[i])) { dbg_printf(1, "libvirt connection %d is dead\n", i); bad++; } } if (info->vp_count < 1 || bad) libvirt_init_libvirt_conf(info); return bad || info->vp_count < 1; } static void libvirt_validate_connections(struct libvirt_info *info) { while (1) { if (libvirt_bad_connections(info)) sleep(1); else break; } } static int libvirt_null(const char *vm_name, void *priv) { dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name); printf("NULL operation: returning failure\n"); return 1; } static int libvirt_off(const char *vm_name, const char *src, uint32_t seqno, void *priv) { struct libvirt_info *info = (struct libvirt_info *)priv; dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno); VALIDATE(info); libvirt_validate_connections(info); return vm_off(info->vp, info->vp_count, vm_name); } static int libvirt_on(const char *vm_name, const char *src, uint32_t seqno, void *priv) { struct libvirt_info *info = (struct libvirt_info *)priv; dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno); VALIDATE(info); libvirt_validate_connections(info); return vm_on(info->vp, info->vp_count, vm_name); } static int libvirt_devstatus(void *priv) { dbg_printf(5, "%s ---\n", __FUNCTION__); if (priv) return 0; return 1; } static int libvirt_status(const char *vm_name, void *priv) { struct libvirt_info *info = (struct libvirt_info *)priv; dbg_printf(5, "ENTER %s %s\n", __FUNCTION__, vm_name); VALIDATE(info); libvirt_validate_connections(info); return vm_status(info->vp, info->vp_count, vm_name); } static int libvirt_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv) { struct libvirt_info *info = (struct libvirt_info *)priv; dbg_printf(5, "ENTER %s %s %u\n", __FUNCTION__, vm_name, seqno); VALIDATE(info); libvirt_validate_connections(info); return vm_reboot(info->vp, info->vp_count, vm_name); } static int libvirt_hostlist(hostlist_callback callback, void *arg, void *priv) { struct libvirt_info *info = (struct libvirt_info *)priv; virt_list_t *vl; int x; dbg_printf(5, "ENTER %s\n", __FUNCTION__); VALIDATE(info); libvirt_validate_connections(info); vl = vl_get(info->vp, info->vp_count, 1); if (!vl) return 0; for (x = 0; x < vl->vm_count; x++) { callback(vl->vm_states[x].v_name, vl->vm_states[x].v_uuid, vl->vm_states[x].v_state.s_state, arg); dbg_printf(10, "[libvirt:HOSTLIST] Sent %s %s %d\n", vl->vm_states[x].v_name, vl->vm_states[x].v_uuid, vl->vm_states[x].v_state.s_state); } vl_free(vl); return 0; } static int libvirt_init(backend_context_t *c, config_object_t *config) { char value[256]; struct libvirt_info *info = NULL; dbg_printf(5, "ENTER [%s:%d %s]\n", __FILE__, __LINE__, __FUNCTION__); info = calloc(1, sizeof(*info)); if (!info) return -1; info->magic = MAGIC; info->config = config; libvirt_init_libvirt_conf(info); #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value)) == 0) dset(atoi(value)); #endif if (info->vp_count < 1) { dbg_printf(1, "[libvirt:INIT] Could not connect to any hypervisors\n"); if (info->vp) free(info->vp); free(info); return -1; } *c = (void *) info; return 0; } static int libvirt_shutdown(backend_context_t c) { struct libvirt_info *info = (struct libvirt_info *)c; int i; int ret = 0; VALIDATE(info); for (i = 0 ; i < info->vp_count ; i++) { if (virConnectClose(info->vp[i]) < 0) ret = -errno; } free(info->vp); free(info); return ret; } static fence_callbacks_t libvirt_callbacks = { .null = libvirt_null, .off = libvirt_off, .on = libvirt_on, .reboot = libvirt_reboot, .status = libvirt_status, .devstatus = libvirt_devstatus, .hostlist = libvirt_hostlist }; static backend_plugin_t libvirt_plugin = { .name = NAME, .version = LIBVIRT_VERSION, .callbacks = &libvirt_callbacks, .init = libvirt_init, .cleanup = libvirt_shutdown, }; #ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &libvirt_plugin; } #else static void __attribute__((constructor)) libvirt_register_plugin(void) { plugin_reg_backend(&libvirt_plugin); } #endif fence-virt-1.0.0/server/main.c000066400000000000000000000133041356743026700161660ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "debug.h" /* configure.c */ int do_configure(config_object_t *config, const char *filename); int daemon_init(const char *prog, const char *pid_file, int nofork); int daemon_cleanup(void); void usage(void) { printf("Usage: fence_virtd [options]\n"); printf(" -F Do not daemonize.\n"); printf(" -f Use as configuration file.\n"); printf(" -d Set debugging level to .\n"); printf(" -c Configuration mode.\n"); printf(" -l List plugins.\n"); printf(" -w Wait for initialization.\n"); printf(" -p Use to record the active process id.\n"); } static int run = 1; void exit_handler(int sig) { run = 0; } int main(int argc, char **argv) { char val[4096]; char listener_name[80]; char backend_name[80]; const char *config_file = SYSCONFDIR "/fence_virt.conf"; char *pid_file = NULL; config_object_t *config = NULL; map_object_t *map = NULL; const listener_plugin_t *lp; const backend_plugin_t *p; listener_context_t listener_ctx = NULL; backend_context_t backend_ctx = NULL; int debug_set = 0, foreground = 0, wait_for_init = 0; int opt, configure = 0; config = sc_init(); map = map_init(); if (!config || !map) { perror("malloc"); return -1; } while ((opt = getopt(argc, argv, "Ff:d:cwlhp:")) != EOF) { switch(opt) { case 'F': printf("Background mode disabled\n"); foreground = 1; break; case 'f': printf("Using %s\n", optarg); config_file = optarg; break; case 'p': printf("Using %s\n", optarg); pid_file = optarg; break; case 'd': debug_set = atoi(optarg); break; case 'c': configure = 1; break; case 'w': wait_for_init = 1; break; case 'l': plugin_dump(); return 0; case 'h': case '?': usage(); return 0; default: return -1; } } if (configure) { return do_configure(config, config_file); } if (sc_parse(config, config_file) != 0) { printf("Failed to parse %s\n", config_file); return -1; } if (debug_set) { snprintf(val, sizeof(val), "%d", debug_set); sc_set(config, "fence_virtd/@debug", val); } else { if (sc_get(config, "fence_virtd/@debug", val, sizeof(val))==0) debug_set = atoi(val); } dset(debug_set); if (!foreground) { if (sc_get(config, "fence_virtd/@foreground", val, sizeof(val)) == 0) foreground = atoi(val); } if (!wait_for_init) { if (sc_get(config, "fence_virtd/@wait_for_init", val, sizeof(val)) == 0) wait_for_init = atoi(val); if (!wait_for_init) { /* XXX compat */ if (sc_get(config, "fence_virtd/@wait_for_backend", val, sizeof(val)) == 0) wait_for_init = atoi(val); } } if (dget() > 3) sc_dump(config, stdout); if (sc_get(config, "fence_virtd/@backend", backend_name, sizeof(backend_name))) { printf("Failed to determine backend.\n"); printf("%s\n", val); return -1; } dbg_printf(1, "Backend plugin: %s\n", backend_name); if (sc_get(config, "fence_virtd/@listener", listener_name, sizeof(listener_name))) { printf("Failed to determine backend.\n"); printf("%s\n", val); return -1; } dbg_printf(1, "Listener plugin: %s\n", listener_name); #ifdef _MODULE if (sc_get(config, "fence_virtd/@module_path", val, sizeof(val))) { #ifdef MODULE_PATH snprintf(val, sizeof(val), MODULE_PATH); #else printf("Failed to determine module path.\n"); return -1; #endif } dbg_printf(1, "Searching %s for plugins...\n", val); opt = plugin_search(val); if (opt > 0) { dbg_printf(1, "%d plugins found\n", opt); } else { printf("No plugins found\n"); return 1; } #endif if (dget() > 3) plugin_dump(); lp = plugin_find_listener(listener_name); if (!lp) { printf("Could not find listener \"%s\"\n", listener_name); return 1; } p = plugin_find_backend(backend_name); if (!p) { printf("Could not find backend \"%s\"\n", backend_name); return 1; } if (pid_file == NULL) { pid_file = malloc(PATH_MAX); memset(pid_file, 0, PATH_MAX); snprintf(pid_file, PATH_MAX, "/var/run/%s.pid", basename(argv[0])); } openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON); daemon_init(basename(argv[0]), pid_file, foreground); signal(SIGINT, exit_handler); signal(SIGTERM, exit_handler); signal(SIGQUIT, exit_handler); syslog(LOG_NOTICE, "fence_virtd starting. Listener: %s Backend: %s", listener_name, backend_name); while (p->init(&backend_ctx, config) < 0) { if (!wait_for_init || !run) { if (foreground) { printf("Backend plugin %s failed to initialize\n", backend_name); } syslog(LOG_ERR, "Backend plugin %s failed to initialize\n", backend_name); return 1; } sleep(5); } if (map_load(map, config) < 0) { syslog(LOG_WARNING, "Failed to load static maps\n"); } /* only client we have now is mcast (fence_xvm behavior) */ while (lp->init(&listener_ctx, p->callbacks, config, map, backend_ctx) != 0) { if (!wait_for_init || !run) { if (foreground) { printf("Listener plugin %s failed to initialize\n", listener_name); } syslog(LOG_ERR, "Listener plugin %s failed to initialize\n", listener_name); return 1; } sleep(5); } while (run && lp->dispatch(listener_ctx, NULL) >= 0); syslog(LOG_NOTICE, "fence_virtd shutting down"); map_release(map); sc_release(config); lp->cleanup(listener_ctx); p->cleanup(backend_ctx); daemon_cleanup(); return 0; } fence-virt-1.0.0/server/mcast.c000066400000000000000000000332171356743026700163560ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "mcast.h" #include "tcp.h" #include "debug.h" #include "fdops.h" #include "list.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "history.h" #define NAME "multicast" #define MCAST_VERSION "1.2" #define MCAST_MAGIC 0xabb911a3 #define VALIDATE(info) \ do {\ if (!info || info->magic != MCAST_MAGIC)\ return -EINVAL;\ } while(0) typedef struct _mcast_options { char *addr; char *key_file; int ifindex; int family; unsigned int port; unsigned int hash; unsigned int auth; unsigned int flags; } mcast_options; typedef struct _mcast_info { uint64_t magic; void *priv; map_object_t *map; history_info_t *history; char key[MAX_KEY_LEN]; mcast_options args; const fence_callbacks_t *cb; ssize_t key_len; int mc_sock; int need_kill; } mcast_info; struct mcast_hostlist_arg { map_object_t *map; const char *src; int fd; }; /* * See if we fenced this node recently (successfully) * If so, ignore the request for a few seconds. * * We purge our history when the entries time out. */ static int check_history(void *a, void *b) { fence_req_t *old = a, *current = b; if (old->request == current->request && old->seqno == current->seqno && !strcasecmp((const char *)old->domain, (const char *)current->domain)) { return 1; } return 0; } static int connect_tcp(fence_req_t *req, fence_auth_type_t auth, void *key, size_t key_len) { int fd = -1; struct sockaddr_in sin; struct sockaddr_in6 sin6; char buf[128]; switch(req->family) { case PF_INET: memset(&sin, 0, sizeof(sin)); memcpy(&sin.sin_addr, req->address, sizeof(sin.sin_addr)); sin.sin_family = PF_INET; fd = ipv4_connect(&sin.sin_addr, req->port, 5); if (fd < 0) { printf("Failed to call back\n"); return -1; } break; case PF_INET6: memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6.sin6_addr, req->address, sizeof(sin6.sin6_addr)); sin.sin_family = PF_INET6; fd = ipv6_connect(&sin6.sin6_addr, req->port, 5); memset(buf,0,sizeof(buf)); inet_ntop(PF_INET6, &sin6.sin6_addr, buf, sizeof(buf)); if (fd < 0) { printf("Failed to call back %s\n", buf); return -1; } break; default: printf("Family = %d\n", req->family); return -1; } /* Noops if auth == AUTH_NONE */ if (sock_response(fd, auth, key, key_len, 10) <= 0) { printf("Failed to respond to challenge\n"); close(fd); return -1; } if (sock_challenge(fd, auth, key, key_len, 10) <= 0) { printf("Remote failed challenge\n"); close(fd); return -1; } return fd; } static int mcast_hostlist(const char *vm_name, const char *vm_uuid, int state, void *priv) { struct mcast_hostlist_arg *arg = (struct mcast_hostlist_arg *)priv; host_state_t hinfo; struct timeval tv; int ret; if (map_check(arg->map, arg->src, vm_uuid) == 0) { /* if we don't have access to fence this VM, * we should not see it in a hostlist either */ return 0; } strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1); strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1); hinfo.state = state; tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int mcast_hostlist_begin(int fd) { struct timeval tv; char val = (char)RESP_HOSTLIST; tv.tv_sec = 1; tv.tv_usec = 0; return _write_retry(fd, &val, 1, &tv); } static int mcast_hostlist_end(int fd) { host_state_t hinfo; struct timeval tv; int ret; printf("Sending terminator packet\n"); memset(&hinfo, 0, sizeof(hinfo)); tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int do_fence_request_tcp(fence_req_t *req, mcast_info *info) { char ip_addr_src[1024]; int fd = -1; char response = 1; struct mcast_hostlist_arg arg; fd = connect_tcp(req, info->args.auth, info->key, info->key_len); if (fd < 0) { dbg_printf(2, "Could not send reply to fence request: %s\n", strerror(errno)); goto out; } inet_ntop(req->family, req->address, ip_addr_src, sizeof(ip_addr_src)); dbg_printf(2, "Request %d seqno %d src %s target %s\n", req->request, req->seqno, ip_addr_src, req->domain); switch(req->request) { case FENCE_NULL: response = info->cb->null((char *)req->domain, info->priv); break; case FENCE_ON: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->on((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_OFF: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->off((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_REBOOT: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->reboot((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_STATUS: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->status((char *)req->domain, info->priv); break; case FENCE_DEVSTATUS: response = info->cb->devstatus(info->priv); break; case FENCE_HOSTLIST: arg.map = info->map; arg.src = ip_addr_src; arg.fd = fd; mcast_hostlist_begin(arg.fd); response = info->cb->hostlist(mcast_hostlist, &arg, info->priv); mcast_hostlist_end(arg.fd); break; } dbg_printf(3, "Sending response to caller...\n"); if (_write_retry(fd, &response, 1, NULL) < 0) { perror("write"); } /* XVM shotguns multicast packets, so we want to avoid * acting on the same request multiple times if the first * attempt was successful. */ history_record(info->history, req); out: if (fd != -1) close(fd); return 1; } static int mcast_dispatch(listener_context_t c, struct timeval *timeout) { mcast_info *info; fence_req_t data; fd_set rfds; struct sockaddr_in sin; int len; int n; socklen_t slen; info = (mcast_info *)c; VALIDATE(info); FD_ZERO(&rfds); FD_SET(info->mc_sock, &rfds); n = select((info->mc_sock)+1, &rfds, NULL, NULL, timeout); if (n <= 0) { if (errno == EINTR || errno == EAGAIN) n = 0; else dbg_printf(2, "select: %s\n", strerror(errno)); return n; } slen = sizeof(sin); len = recvfrom(info->mc_sock, &data, sizeof(data), 0, (struct sockaddr *)&sin, &slen); if (len <= 0) { perror("recvfrom"); return len; } swab_fence_req_t(&data); if (!verify_request(&data, info->args.hash, info->key, info->key_len)) { printf("Key mismatch; dropping packet\n"); return 0; } printf("Request %d seqno %d domain %s\n", data.request, data.seqno, data.domain); if (history_check(info->history, &data) == 1) { printf("We just did this request; dropping packet\n"); return 0; } switch(info->args.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: printf("Plain TCP request\n"); do_fence_request_tcp(&data, info); break; default: printf("XXX Unhandled authentication\n"); } return 0; } static int mcast_config(config_object_t *config, mcast_options *args) { char value[1024]; int errors = 0; #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); #endif if (sc_get(config, "listeners/multicast/@key_file", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for key_file\n", value); args->key_file = strdup(value); } else { args->key_file = strdup(DEFAULT_KEY_FILE); if (!args->key_file) { dbg_printf(1, "Failed to allocate memory\n"); return -1; } } args->hash = DEFAULT_HASH; if (sc_get(config, "listeners/multicast/@hash", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for hash\n", value); if (!strcasecmp(value, "none")) { args->hash = HASH_NONE; } else if (!strcasecmp(value, "sha1")) { args->hash = HASH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->hash = HASH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->hash = HASH_SHA512; } else { dbg_printf(1, "Unsupported hash: %s\n", value); ++errors; } } args->auth = DEFAULT_AUTH; if (sc_get(config, "listeners/multicast/@auth", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for auth\n", value); if (!strcasecmp(value, "none")) { args->auth = AUTH_NONE; } else if (!strcasecmp(value, "sha1")) { args->auth = AUTH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->auth = AUTH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->auth = AUTH_SHA512; } else { dbg_printf(1, "Unsupported auth: %s\n", value); ++errors; } } args->family = PF_INET; if (sc_get(config, "listeners/multicast/@family", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for family\n", value); if (!strcasecmp(value, "ipv4")) { args->family = PF_INET; } else if (!strcasecmp(value, "ipv6")) { args->family = PF_INET6; } else { dbg_printf(1, "Unsupported family: %s\n", value); ++errors; } } if (sc_get(config, "listeners/multicast/@address", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for address\n", value); args->addr = strdup(value); } else { if (args->family == PF_INET) { args->addr = strdup(IPV4_MCAST_DEFAULT); } else { args->addr = strdup(IPV6_MCAST_DEFAULT); } } if (!args->addr) { return -1; } args->port = DEFAULT_MCAST_PORT; if (sc_get(config, "listeners/multicast/@port", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for port\n", value); args->port = atoi(value); if (args->port <= 0) { dbg_printf(1, "Invalid port: %s\n", value); ++errors; } } args->ifindex = 0; if (sc_get(config, "listeners/multicast/@interface", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for interface\n", value); args->ifindex = if_nametoindex(value); if (args->ifindex < 0) { dbg_printf(1, "Invalid interface: %s\n", value); ++errors; } } return errors; } static int mcast_init(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv) { mcast_info *info; int mc_sock, ret; /* Initialize NSS; required to do hashing, as silly as that sounds... */ if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } info = malloc(sizeof(*info)); if (!info) return -1; memset(info, 0, sizeof(*info)); info->priv = priv; info->cb = cb; info->map = map; ret = mcast_config(config, &info->args); if (ret < 0) { perror("mcast_config"); free(info); return -1; } else if (ret > 0) { printf("%d errors found during configuration\n",ret); free(info); return -1; } if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) { info->key_len = read_key_file(info->args.key_file, info->key, sizeof(info->key)); if (info->key_len < 0) { printf("Could not read %s; operating without " "authentication\n", info->args.key_file); info->args.auth = AUTH_NONE; info->args.hash = HASH_NONE; info->key_len = 0; } } if (info->args.family == PF_INET) mc_sock = ipv4_recv_sk(info->args.addr, info->args.port, info->args.ifindex); else mc_sock = ipv6_recv_sk(info->args.addr, info->args.port, info->args.ifindex); if (mc_sock < 0) { printf("Could not set up multicast listen socket\n"); free(info); return -1; } info->magic = MCAST_MAGIC; info->mc_sock = mc_sock; info->history = history_init(check_history, 10, sizeof(fence_req_t)); *c = (listener_context_t)info; return 0; } static int mcast_shutdown(listener_context_t c) { mcast_info *info = (mcast_info *)c; VALIDATE(info); info->magic = 0; history_wipe(info->history); free(info->history); free(info->args.key_file); free(info->args.addr); close(info->mc_sock); free(info); return 0; } static listener_plugin_t mcast_plugin = { .name = NAME, .version = MCAST_VERSION, .init = mcast_init, .dispatch = mcast_dispatch, .cleanup = mcast_shutdown, }; #ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &mcast_plugin; } #else static void __attribute__((constructor)) mcast_register_plugin(void) { plugin_reg_listener(&mcast_plugin); } #endif fence-virt-1.0.0/server/null.c000066400000000000000000000073501356743026700162200ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 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, 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #define NAME "null" #define NULL_VERSION "0.8" #define MAGIC 0x1e00017a struct null_info { int magic; int pad; char *message; }; #define VALIDATE(arg) \ do {\ if (!arg || ((struct null_info *)arg)->magic != MAGIC) { \ errno = EINVAL;\ return -1; \ } \ } while(0) static int null_null(const char *vm_name, void *priv) { VALIDATE(priv); printf("[Null] Null operation on %s\n", vm_name); return 1; } static int null_off(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[Null] OFF operation on %s\n", vm_name); return 1; } static int null_on(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[Null] ON operation on %s\n", vm_name); return 1; } static int null_devstatus(void *priv) { printf("[Null] Device status\n"); VALIDATE(priv); printf("[Null] Message for you: %s\n", ((struct null_info *)priv)->message); return 0; } static int null_status(const char *vm_name, void *priv) { VALIDATE(priv); printf("[Null] STATUS operation on %s\n", vm_name); return 1; } static int null_reboot(const char *vm_name, const char *src, uint32_t seqno, void *priv) { VALIDATE(priv); printf("[Null] REBOOT operation on %s\n", vm_name); return 1; } static int null_hostlist(hostlist_callback callback, void *arg, void *priv) { VALIDATE(priv); printf("[Null] HOSTLIST operation\n"); return 1; } static int null_init(backend_context_t *c, config_object_t *config) { char value[256]; struct null_info *info = NULL; char *null_message = NULL; info = malloc(sizeof(*info)); if (!info) return -1; memset(info, 0, sizeof(*info)); if (sc_get(config, "backends/null/@message", value, sizeof(value)) != 0) { snprintf(value, sizeof(value), "Hi!"); } null_message = strdup(value); if (!null_message) { free(info); return -1; } info->magic = MAGIC; info->message = null_message; *c = (void *)info; return 0; } static int null_shutdown(backend_context_t c) { struct null_info *info = (struct null_info *)c; VALIDATE(info); info->magic = 0; free(info->message); free(info); return 0; } static fence_callbacks_t null_callbacks = { .null = null_null, .off = null_off, .on = null_on, .reboot = null_reboot, .status = null_status, .devstatus = null_devstatus, .hostlist = null_hostlist }; static backend_plugin_t null_plugin = { .name = NAME, .version = NULL_VERSION, .callbacks = &null_callbacks, .init = null_init, .cleanup = null_shutdown, }; #ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &null_plugin; } #else static void __attribute__((constructor)) null_register_plugin(void) { plugin_reg_backend(&null_plugin); } #endif fence-virt-1.0.0/server/plugin.c000066400000000000000000000205401356743026700165400ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2002-2004, 2009 The Magma Cluster API Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The Magma Cluster API Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /** @file * Plugin loading routines */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "debug.h" typedef struct _plugin_list { list_head(); const listener_plugin_t *listener; const backend_plugin_t *backend; plugin_type_t type; } plugin_list_t; static plugin_list_t *server_plugins = NULL; int plugin_reg_backend(const backend_plugin_t *plugin) { plugin_list_t *newplug; if (plugin_find_backend(plugin->name)) { errno = EEXIST; return -1; } newplug = malloc(sizeof(*newplug)); if (!newplug) return -1; memset(newplug, 0, sizeof(*newplug)); newplug->backend = plugin; newplug->type = PLUGIN_BACKEND; list_insert(&server_plugins, newplug); return 0; } int plugin_reg_listener(const listener_plugin_t *plugin) { plugin_list_t *newplug; if (plugin_find_listener(plugin->name)) { errno = EEXIST; return -1; } newplug = malloc(sizeof(*newplug)); if (!newplug) return -1; memset(newplug, 0, sizeof(*newplug)); newplug->listener = plugin; newplug->type = PLUGIN_LISTENER; list_insert(&server_plugins, newplug); return 0; } void plugin_dump(void) { plugin_list_t *p; int x, y; y = 0; list_for(&server_plugins, p, x) { if (p->type == PLUGIN_BACKEND) { if (!y) { y = 1; printf("Available backends:\n"); } printf(" %s %s\n", p->backend->name, p->backend->version); } } y = 0; list_for(&server_plugins, p, x) { if (p->type == PLUGIN_LISTENER) { if (!y) { y = 1; printf("Available listeners:\n"); } printf(" %s %s\n", p->listener->name, p->listener->version); } } } const backend_plugin_t * plugin_find_backend(const char *name) { plugin_list_t *p; int x; list_for(&server_plugins, p, x) { if (p->type != PLUGIN_BACKEND) continue; if (!strcasecmp(name, p->backend->name)) return p->backend; } return NULL; } const listener_plugin_t * plugin_find_listener(const char *name) { plugin_list_t *p; int x; list_for(&server_plugins, p, x) { if (p->type != PLUGIN_LISTENER) continue; if (!strcasecmp(name, p->listener->name)) return p->listener; } return NULL; } static int backend_plugin_load(void *handle, const char *libpath) { const backend_plugin_t *plug = NULL; double (*modversion)(void); backend_plugin_t *(*modinfo)(void); modversion = dlsym(handle, BACKEND_VER_STR); if (!modversion) { dbg_printf(1, "Failed to map %s\n", BACKEND_VER_STR); errno = EINVAL; return -1; } if (modversion() != PLUGIN_VERSION_BACKEND) { dbg_printf(1, "API version mismatch in %s: \n" " %f expected; %f received.\n", libpath, PLUGIN_VERSION_BACKEND, modversion()); errno = EINVAL; return -1; } modinfo = dlsym(handle, BACKEND_INFO_STR); if (!modinfo) { dbg_printf(1, "Failed to map %s\n", BACKEND_INFO_STR); errno = EINVAL; return -1; } plug = modinfo(); if (plugin_reg_backend(plug) < 0) { dbg_printf(1, "Failed to register %s %s\n", plug->name, plug->version); errno = EINVAL; return -1; } else { dbg_printf(1, "Registered backend plugin %s %s\n", plug->name, plug->version); } return 0; } static int listener_plugin_load(void *handle, const char *libpath) { const listener_plugin_t *plug = NULL; double (*modversion)(void); listener_plugin_t *(*modinfo)(void); modversion = dlsym(handle, LISTENER_VER_STR); if (!modversion) { dbg_printf(1, "Failed to map %s\n", LISTENER_VER_STR); errno = EINVAL; return -1; } if (modversion() != PLUGIN_VERSION_LISTENER) { dbg_printf(1, "API version mismatch in %s: \n" " %f expected; %f received.\n", libpath, PLUGIN_VERSION_LISTENER, modversion()); dlclose(handle); errno = EINVAL; return -1; } modinfo = dlsym(handle, LISTENER_INFO_STR); if (!modinfo) { dbg_printf(1, "Failed to map %s\n", LISTENER_INFO_STR); errno = EINVAL; return -1; } plug = modinfo(); if (plugin_reg_listener(plug) < 0) { dbg_printf(1, "Failed to register %s %s\n", plug->name, plug->version); errno = EINVAL; return -1; } else { dbg_printf(1, "Registered listener plugin %s %s\n", plug->name, plug->version); } return 0; } /** * Load a cluster plugin .so file and map all the functions * provided to entries in a backend_plugin_t structure. * * @param libpath Path to file. * @return NULL on failure, or plugin-specific * (const) backend_plugin_t * structure on * success. */ int plugin_load(const char *libpath) { void *handle = NULL; struct stat sb; errno = 0; if (!libpath) { errno = EINVAL; return -1; } if (stat(libpath, &sb) != 0) { return -1; } /* If it's not owner-readable or it's a directory, ignore/fail. Thus, a user may change the permission of a plugin "u-r" and this would then prevent magma apps from loading it. */ if (S_ISDIR(sb.st_mode)) { errno = EISDIR; return -1; } if (!(sb.st_mode & S_IRUSR)) { dbg_printf(1, "Ignoring %s (User-readable bit not set)\n", libpath); errno = EPERM; return -1; } dbg_printf(3, "Loading plugin from %s\n", libpath); handle = dlopen(libpath, RTLD_NOW); if (!handle) { dbg_printf(3, "Could not dlopen %s: %s\n", libpath, dlerror()); errno = ELIBACC; return -1; } if (!backend_plugin_load(handle, libpath) || !listener_plugin_load(handle, libpath)) return 0; dbg_printf(3, "%s is not a valid plugin\n", libpath); dlclose(handle); errno = EINVAL; return -1; } /** Free up a null-terminated array of strings */ static void free_dirnames(char **dirnames) { int x = 0; for (; dirnames[x]; x++) free(dirnames[x]); free(dirnames); } static int _compare(const void *a, const void *b) { return strcmp((const char *)a, (const char *)b); } /** Read all entries in a directory and return them in a NULL-terminated, sorted array. */ static int read_dirnames_sorted(const char *directory, char ***dirnames) { DIR *dir; struct dirent *entry; char filename[1024]; int count = 0, x = 0; dir = opendir(directory); if (!dir) return -1; /* Count the number of plugins */ while ((entry = readdir(dir)) != NULL) ++count; /* Malloc the entries */ *dirnames = malloc(sizeof(char *) * (count+1)); if (!*dirnames) { #ifdef DEBUG printf("%s: Failed to malloc %d bytes", __FUNCTION__, (int)(sizeof(char *) * (count+1))); #endif closedir(dir); errno = ENOMEM; return -1; } memset(*dirnames, 0, sizeof(char *) * (count + 1)); rewinddir(dir); /* Store the directory names. */ while ((entry = readdir(dir)) != NULL) { snprintf(filename, sizeof(filename), "%s/%s", directory, entry->d_name); (*dirnames)[x] = strdup(filename); if (!(*dirnames)[x]) { #ifdef DEBUG printf("Failed to duplicate %s\n", filename); #endif free_dirnames(*dirnames); closedir(dir); errno = ENOMEM; return -1; } ++x; } closedir(dir); /* Sort the directory names. */ qsort((*dirnames), count, sizeof(char *), _compare); return 0; } /** */ int plugin_search(const char *pathname) { int found = 0; int fcount = 0; char **filenames; dbg_printf(1, "Searching for plugins in %s\n", pathname); if (read_dirnames_sorted(pathname, &filenames) != 0) { return -1; } for (fcount = 0; filenames[fcount]; fcount++) { if (plugin_load(filenames[fcount]) == 0) ++found; } free_dirnames(filenames); if (!found) { dbg_printf(1, "No usable plugins found.\n"); errno = ELIBACC; return -1; } return found; } fence-virt-1.0.0/server/pm-fence.c000066400000000000000000000353021356743026700167360ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "debug.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #define BACKEND_NAME "pm-fence" #define VERSION "0.1" #define MAGIC 0x1e0d197a #define ATTR_NAME_PREFIX "force_stop-" #define ATTR_VALUE "true" #define READ_CIB_RETRY 30 enum rsc_status { RS_STARTED = 1, RS_STOPPED, RS_UNDEFINED, RS_GETERROR }; struct pf_info { int magic; cib_t *cib; unsigned int loglevel; }; cib_t **cib = NULL; pe_working_set_t data_set; #define VALIDATE(arg) \ do { \ if (!arg || ((struct pf_info *)arg)->magic != MAGIC) { \ errno = EINVAL; \ return -1; \ } \ } while(0) static void free_dataset(void) { dbg_printf(5, "%s\n", __FUNCTION__); if (!data_set.input) return; free_xml(data_set.input); data_set.input = NULL; cleanup_calculations(&data_set); memset(&data_set, 0, sizeof(pe_working_set_t)); } static void disconnect_cib(void) { dbg_printf(5, "%s\n", __FUNCTION__); if (*cib) { (*cib)->cmds->signoff(*cib); cib_delete(*cib); *cib = NULL; } free_dataset(); } static gboolean connect_cib(void) { enum cib_errors rc = cib_ok; int i; dbg_printf(5, "%s\n", __FUNCTION__); if (*cib) return TRUE; memset(&data_set, 0, sizeof(pe_working_set_t)); *cib = cib_new(); if (!*cib) { syslog(LOG_NOTICE, "cib connection initialization failed\n"); printf("cib connection initialization failed\n"); return FALSE; } for (i = 1; i <= 20; i++) { if (i) sleep(1); dbg_printf(4, "%s: connect to cib attempt: %d\n", __FUNCTION__, i); rc = (*cib)->cmds->signon(*cib, crm_system_name, cib_command); if (rc == cib_ok) break; } if (rc != cib_ok) { syslog(LOG_NOTICE, "failed to signon to cib: %s\n", cib_error2string(rc)); printf("failed to signon to cib: %s\n", cib_error2string(rc)); disconnect_cib(); return FALSE; } dbg_printf(3, "%s: succeed at connect to cib\n", __FUNCTION__); return TRUE; } static gboolean get_dataset(void) { xmlNode *current_cib; unsigned int loglevel; dbg_printf(5, "%s\n", __FUNCTION__); free_dataset(); current_cib = get_cib_copy(*cib); if (!current_cib) return FALSE; set_working_set_defaults(&data_set); data_set.input = current_cib; data_set.now = new_ha_date(TRUE); /* log output of the level below LOG_ERR is deterred */ loglevel = get_crm_log_level(); set_crm_log_level(LOG_ERR); cluster_status(&data_set); set_crm_log_level(loglevel); return TRUE; } static enum rsc_status get_rsc_status(const char *rid, char **node, char **uuid) { GListPtr gIter = NULL, gIter2 = NULL; resource_t *rsc; dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); if (!rid || connect_cib() == FALSE) return RS_GETERROR; if (get_dataset() == FALSE) { disconnect_cib(); if (connect_cib() == FALSE || get_dataset() == FALSE) return RS_GETERROR; } /* find out from RUNNING resources */ gIter = data_set.nodes; for(; gIter; gIter = gIter->next) { node_t *node2 = (node_t*)gIter->data; gIter2 = node2->details->running_rsc; for(; gIter2; gIter2 = gIter2->next) { resource_t *rsc2 = (resource_t*)gIter2->data; dbg_printf(3, "%s: started resource [%s]\n", __FUNCTION__, rsc2->id); if (safe_str_eq(rid, rsc2->id)) { if (node && !*node) { *node = crm_strdup(node2->details->uname); *uuid = crm_strdup(node2->details->id); dbg_printf(3, "%s: started node [%s(%s)]\n", __FUNCTION__, *node, *uuid); } return RS_STARTED; } } } /* find out from ALL resources */ rsc = pe_find_resource(data_set.resources, rid); if (rsc) { dbg_printf(3, "%s: stopped resource [%s]\n", __FUNCTION__, rsc->id); return RS_STOPPED; } return RS_UNDEFINED; } /* * The cluster node attribute is updated for RA which controls a virtual machine. */ static gboolean update_status_attr(char cmd, const char *rid, const char *node, const char *uuid, gboolean confirm) { char *name = g_strdup_printf("%s%s", ATTR_NAME_PREFIX, rid); char *value; gboolean ret = FALSE; dbg_printf(5, "%s\n", __FUNCTION__); switch (cmd) { case 'U': value = ATTR_VALUE; break; case 'D': value = NULL; break; default: goto out; } dbg_printf(1, "%s: Update attribute %s=%s for %s\n", __FUNCTION__, name, value, node); ret = attrd_lazy_update(cmd, node, name, value, XML_CIB_TAG_STATUS, NULL, NULL); if (confirm == FALSE) goto out; if (ret == TRUE) { enum cib_errors rc; int i; ret = FALSE; value = NULL; for (i = 1; i <= READ_CIB_RETRY; i++) { dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i); sleep(1); #ifdef PM_1_0 rc = read_attr(*cib, XML_CIB_TAG_STATUS, uuid, NULL, NULL, name, &value, FALSE); #else rc = read_attr(*cib, XML_CIB_TAG_STATUS, uuid, NULL, NULL, NULL, name, &value, FALSE); #endif dbg_printf(3, "%s: cmd=%c, rc=%d, value=%s\n", __FUNCTION__, cmd, rc, value); if (rc == cib_ok) { if (cmd == 'U' && !g_strcmp0(value, ATTR_VALUE)) { ret = TRUE; break; } } else if (rc == cib_NOTEXISTS) { if (cmd == 'D') { ret = TRUE; break; } } else { break; } crm_free(value); } crm_free(value); } out: crm_free(name); return ret; } /* * ref. pacemaker/tools/crm_resource.c */ static enum cib_errors find_meta_attr(const char *rid, const char *name, char **id) { char *xpath; xmlNode *xml = NULL; const char *p; enum cib_errors rc; dbg_printf(5, "%s\n", __FUNCTION__); xpath = g_strdup_printf("%s/*[@id=\"%s\"]/%s/nvpair[@name=\"%s\"]", get_object_path("resources"), rid, XML_TAG_META_SETS, name); dbg_printf(3, "%s: query=%s\n", __FUNCTION__, xpath); rc = (*cib)->cmds->query(*cib, xpath, &xml, cib_sync_call|cib_scope_local|cib_xpath); if (rc != cib_ok) { if (rc != cib_NOTEXISTS) { syslog(LOG_NOTICE, "failed to query to cib: %s\n", cib_error2string(rc)); printf("failed to query to cib: %s\n", cib_error2string(rc)); } crm_free(xpath); return rc; } crm_log_xml_debug(xml, "Match"); p = crm_element_value(xml, XML_ATTR_ID); if (p) *id = crm_strdup(p); crm_free(xpath); free_xml(xml); return rc; } /* * ref. pacemaker/tools/crm_resource.c */ static gboolean set_rsc_role(const char *rid, const char *value) { resource_t *rsc; char *id = NULL; xmlNode *top = NULL, *obj = NULL; enum cib_errors rc; const char *name = XML_RSC_ATTR_TARGET_ROLE; dbg_printf(5, "%s\n", __FUNCTION__); rsc = pe_find_resource(data_set.resources, rid); if (!rsc) return FALSE; rc = find_meta_attr(rid, name, &id); if (rc == cib_ok) { dbg_printf(3, "%s: Found a match for name=%s: id=%s\n", __FUNCTION__, name, id); } else if (rc == cib_NOTEXISTS) { char *set; set = crm_concat(rid, XML_TAG_META_SETS, '-'); id = crm_concat(set, name, '-'); top = create_xml_node(NULL, crm_element_name(rsc->xml)); crm_xml_add(top, XML_ATTR_ID, rid); obj = create_xml_node(top, XML_TAG_META_SETS); crm_xml_add(obj, XML_ATTR_ID, set); crm_free(set); } else { return FALSE; } obj = create_xml_node(obj, XML_CIB_TAG_NVPAIR); if (!top) top = obj; crm_xml_add(obj, XML_ATTR_ID, id); crm_xml_add(obj, XML_NVPAIR_ATTR_NAME, name); crm_xml_add(obj, XML_NVPAIR_ATTR_VALUE, value); dbg_printf(1, "%s: Update meta-attr %s=%s for %s\n", __FUNCTION__, name, value, rid); crm_log_xml_debug(top, "Update"); rc = (*cib)->cmds->modify(*cib, XML_CIB_TAG_RESOURCES, top, cib_sync_call); if (rc != cib_ok) { syslog(LOG_NOTICE, "failed to modify to cib: %s\n", cib_error2string(rc)); printf("failed to modify to cib: %s\n", cib_error2string(rc)); } free_xml(top); crm_free(id); return rc == cib_ok ? TRUE : FALSE; } static gboolean start_resource(const char *rid) { gboolean updated_cib = FALSE; int i = 0; dbg_printf(5, "%s\n", __FUNCTION__); if (!rid) return FALSE; printf("Starting domain %s(resource)\n", rid); check: if (i >= READ_CIB_RETRY) return FALSE; switch (get_rsc_status(rid, NULL, NULL)) { case RS_STARTED: dbg_printf(2, "%s: Resource %s started\n", __FUNCTION__, rid); return TRUE; case RS_STOPPED: if (updated_cib == FALSE) { if (set_rsc_role(rid, RSC_ROLE_STARTED_S) == FALSE) return FALSE; updated_cib = TRUE; } else { i++; } dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i); sleep(1); goto check; default: return FALSE; } } static gboolean stop_resource(const char *rid) { char *node = NULL, *uuid = NULL; gboolean updated_cib = FALSE; gboolean ret = FALSE; int i = 0; dbg_printf(5, "%s\n", __FUNCTION__); if (!rid) return FALSE; printf("Destroying domain %s(resource)\n", rid); check: if (i >= READ_CIB_RETRY) goto rollback; switch (get_rsc_status(rid, &node, &uuid)) { case RS_STARTED: if (updated_cib == FALSE) { if (update_status_attr('U', rid, node, uuid, TRUE) == FALSE) goto out; if (set_rsc_role(rid, RSC_ROLE_STOPPED_S) == FALSE) goto rollback; updated_cib = TRUE; } else { i++; } dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i); sleep(1); goto check; case RS_STOPPED: dbg_printf(2, "%s: Resource %s stopped\n", __FUNCTION__, rid); if (updated_cib == FALSE) ret = TRUE; else ret = update_status_attr('D', rid, node, uuid, TRUE); goto out; default: goto out; } rollback: update_status_attr('D', rid, node, uuid, FALSE); out: if (node) crm_free(node); if (uuid) crm_free(uuid); return ret; } static int char2level(const char *str) { dbg_printf(5, "%s\n", __FUNCTION__); if (!str) return 0; if (safe_str_eq(str, "emerg")) return LOG_EMERG; else if (safe_str_eq(str, "alert")) return LOG_ALERT; else if (safe_str_eq(str, "crit")) return LOG_CRIT; else if (safe_str_eq(str, "err") || safe_str_eq(str, "error")) return LOG_ERR; else if (safe_str_eq(str, "warning") || safe_str_eq(str, "warn")) return LOG_WARNING; else if (safe_str_eq(str, "notice")) return LOG_NOTICE; else if (safe_str_eq(str, "info")) return LOG_INFO; else if (safe_str_eq(str, "debug")) return LOG_DEBUG; else if (safe_str_eq(str, "debug2")) return LOG_DEBUG + 1; else if (safe_str_eq(str, "debug3")) return LOG_DEBUG + 2; else if (safe_str_eq(str, "debug4")) return LOG_DEBUG + 3; else if (safe_str_eq(str, "debug5")) return LOG_DEBUG + 4; else if (safe_str_eq(str, "debug6")) return LOG_DEBUG + 5; return 0; } static void reset_lib_log(unsigned int level) { dbg_printf(5, "%s\n", __FUNCTION__); cl_log_set_entity(BACKEND_NAME); set_crm_log_level(level); } static int pf_null(const char *rid, void *priv) { dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); printf("NULL operation: returning failure\n"); return 1; } static int pf_off(const char *rid, const char *src, uint32_t seqno, void *priv) { struct pf_info *info = (struct pf_info *)priv; int ret; dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); VALIDATE(info); reset_lib_log(info->loglevel); cib = &info->cib; ret = stop_resource(rid) == TRUE ? 0 : 1; free_dataset(); return ret; } static int pf_on(const char *rid, const char *src, uint32_t seqno, void *priv) { struct pf_info *info = (struct pf_info *)priv; int ret; dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); VALIDATE(info); reset_lib_log(info->loglevel); cib = &info->cib; ret = start_resource(rid) == TRUE ? 0 : 1; free_dataset(); return ret; } static int pf_devstatus(void *priv) { dbg_printf(5, "%s\n", __FUNCTION__); if (priv) return 0; return 1; } static int pf_status(const char *rid, void *priv) { struct pf_info *info = (struct pf_info *)priv; enum rsc_status rstat; dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); VALIDATE(info); reset_lib_log(info->loglevel); cib = &info->cib; rstat = get_rsc_status(rid, NULL, NULL); dbg_printf(3, "%s: get_rsc_status [%d]\n", __FUNCTION__, rstat); free_dataset(); switch (rstat) { case RS_STARTED: return RESP_SUCCESS; case RS_STOPPED: return RESP_OFF; case RS_UNDEFINED: case RS_GETERROR: default: return RESP_FAIL; } } static int pf_reboot(const char *rid, const char *src, uint32_t seqno, void *priv) { struct pf_info *info = (struct pf_info *)priv; int ret = 1; dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid); VALIDATE(info); reset_lib_log(info->loglevel); cib = &info->cib; if (stop_resource(rid) == TRUE) ret = start_resource(rid) == TRUE ? 0 : ret; free_dataset(); return ret; } /* * Not implemented, because it is not called from the STONITH plug-in. */ static int pf_hostlist(hostlist_callback callback, void *arg, void *priv) { struct pf_info *info = (struct pf_info *)priv; dbg_printf(5, "%s\n", __FUNCTION__); VALIDATE(info); return 1; } static int pf_init(backend_context_t *c, config_object_t *conf) { struct pf_info *info = NULL; int level = 0; char value[256]; char key[32]; dbg_printf(5, "%s\n", __FUNCTION__); #ifdef _MODULE if (sc_get(conf, "fence_virtd/@debug", value, sizeof(value)) == 0) dset(atoi(value)); #endif sprintf(key, "backends/%s/@pmlib_loglevel", BACKEND_NAME); if (sc_get(conf, key, value, sizeof(value)) == 0) { level = char2level(value); crm_log_init(BACKEND_NAME, level, FALSE, FALSE, 0, NULL); cl_log_enable_stdout(TRUE); } info = malloc(sizeof(*info)); if (!info) return -1; memset(info, 0, sizeof(*info)); info->magic = MAGIC; info->loglevel = level; *c = (void *)info; return 0; } static int pf_shutdown(backend_context_t c) { struct pf_info *info = (struct pf_info *)c; dbg_printf(5, "%s\n", __FUNCTION__); VALIDATE(info); reset_lib_log(info->loglevel); cib = &info->cib; disconnect_cib(); free(info); return 0; } static fence_callbacks_t pf_callbacks = { .null = pf_null, .off = pf_off, .on = pf_on, .reboot = pf_reboot, .status = pf_status, .devstatus = pf_devstatus, .hostlist = pf_hostlist }; static backend_plugin_t pf_plugin = { .name = BACKEND_NAME, .version = VERSION, .callbacks = &pf_callbacks, .init = pf_init, .cleanup = pf_shutdown, }; #ifdef _MODULE double BACKEND_VER_SYM(void) { return PLUGIN_VERSION_BACKEND; } const backend_plugin_t * BACKEND_INFO_SYM(void) { return &pf_plugin; } #else static void __attribute__((constructor)) pf_register_plugin(void) { plugin_reg_backend(&pf_plugin); } #endif fence-virt-1.0.0/server/serial.c000066400000000000000000000231171356743026700165240ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2010 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Lon Hohberger */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "debug.h" #include "fdops.h" #include "serial.h" #include "list.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "history.h" #include "xvm.h" #define NAME "serial" #define SERIAL_VERSION "0.4" #define SERIAL_PLUG_MAGIC 0x1227a000 #define VALIDATE(info) \ do {\ if (!info || info->magic != SERIAL_PLUG_MAGIC)\ return -EINVAL;\ } while(0) typedef struct _serial_info { uint64_t magic; const fence_callbacks_t *cb; void *priv; char *uri; char *path; history_info_t *history; map_object_t *maps; int mode; int wake_fd; } serial_info; struct serial_hostlist_arg { map_object_t *map; const char *src; int fd; }; /* * See if we fenced this node recently (successfully) * If so, ignore the request for a few seconds. * * We purge our history when the entries time out. */ static int check_history(void *a, void *b) { serial_req_t *old = a, *current = b; if (old->request == current->request && old->seqno == current->seqno && !strcasecmp((const char *)old->domain, (const char *)current->domain)) { return 1; } return 0; } static int serial_hostlist(const char *vm_name, const char *vm_uuid, int state, void *priv) { struct serial_hostlist_arg *arg = (struct serial_hostlist_arg *)priv; host_state_t hinfo; struct timeval tv; int ret; if (map_check(arg->map, arg->src, vm_uuid) == 0) { /* if we don't have access to fence this VM, * we should not see it in a hostlist either */ return 0; } strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1); strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1); hinfo.state = state; tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int serial_hostlist_begin(int fd) { struct timeval tv; serial_resp_t resp; resp.magic = SERIAL_MAGIC; resp.response = RESP_HOSTLIST; tv.tv_sec = 1; tv.tv_usec = 0; return _write_retry(fd, &resp, sizeof(resp), &tv); } static int serial_hostlist_end(int fd) { host_state_t hinfo; struct timeval tv; int ret; //printf("Sending terminator packet\n"); memset(&hinfo, 0, sizeof(hinfo)); tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int do_fence_request(int fd, const char *src, serial_req_t *req, serial_info *info) { char response = RESP_FAIL; struct serial_hostlist_arg arg; serial_resp_t resp; arg.fd = fd; switch(req->request) { case FENCE_NULL: response = info->cb->null((char *)req->domain, info->priv); break; case FENCE_ON: if (map_check(info->maps, src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->on((char *)req->domain, src, req->seqno, info->priv); break; case FENCE_OFF: if (map_check(info->maps, src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->off((char *)req->domain, src, req->seqno, info->priv); break; case FENCE_REBOOT: if (map_check(info->maps, src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->reboot((char *)req->domain, src, req->seqno, info->priv); break; case FENCE_STATUS: if (map_check(info->maps, src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->status((char *)req->domain, info->priv); break; case FENCE_DEVSTATUS: response = info->cb->devstatus(info->priv); break; case FENCE_HOSTLIST: arg.map = info->maps; arg.src = src; arg.fd = fd; serial_hostlist_begin(arg.fd); response = info->cb->hostlist(serial_hostlist, &arg, info->priv); serial_hostlist_end(arg.fd); break; } resp.magic = SERIAL_MAGIC; resp.response = response; swab_serial_resp_t(&resp); dbg_printf(3, "Sending response to caller...\n"); if (_write_retry(fd, &resp, sizeof(resp), NULL) < 0) perror("write"); /* XVM shotguns multicast packets, so we want to avoid * acting on the same request multiple times if the first * attempt was successful. */ history_record(info->history, req); return 1; } static int serial_dispatch(listener_context_t c, struct timeval *timeout) { char src_domain[MAX_DOMAINNAME_LENGTH]; serial_info *info; serial_req_t data; fd_set rfds; struct timeval tv; int max; int n, x, ret; info = (serial_info *)c; VALIDATE(info); FD_ZERO(&rfds); domain_sock_fdset(&rfds, &max); FD_SET(info->wake_fd, &rfds); if (info->wake_fd > max) max = info->wake_fd; n = select(max+1, &rfds, NULL, NULL, timeout); if (n <= 0) { if (errno == EINTR || errno == EAGAIN) n = 0; else dbg_printf(2, "select: %s\n", strerror(errno)); return n; } /* * See if the goal was just to be woken up in order to refill our * file descriptor set. For example, if multiple domains were * created simultaneously, we would have to refill our fd_set */ if (FD_ISSET(info->wake_fd, &rfds)) { tv.tv_sec = 0; tv.tv_usec = 10000; _read_retry(info->wake_fd, &c, 1, &tv); return 0; } /* * If no requests, we're done */ if (n == 0) return 0; /* find & read request */ for (x = 0; x <= max; x++) { if (FD_ISSET(x, &rfds)) { tv.tv_sec = 1; tv.tv_usec = 0; ret = _read_retry(x, &data, sizeof(data), &tv); if (ret != sizeof(data)) { if (--n > 0) continue; else return 0; } else { swab_serial_req_t(&data); break; } } } src_domain[0] = 0; domain_sock_name(x, src_domain, sizeof(src_domain)); dbg_printf(2, "Sock %d Request %d seqno %d src %s target %s\n", x, data.request, data.seqno, src_domain, data.domain); if (history_check(info->history, &data) == 1) { dbg_printf(3, "We just did this request; dropping packet\n"); return 0; } do_fence_request(x, src_domain[0] == 0 ? NULL : src_domain, &data, info); return 0; } static int serial_config(config_object_t *config, serial_info *args) { char value[1024]; int errors = 0; #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); #endif if (sc_get(config, "listeners/serial/@uri", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for uri\n", value); args->uri = strdup(value); } if (sc_get(config, "listeners/serial/@path", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for uri\n", value); args->path = strdup(value); } if (sc_get(config, "listeners/serial/@mode", value, sizeof(value)-1) == 0) { if (!strcasecmp(value, "vmchannel")) { args->mode = 1; } else if (!strcasecmp(value, "serial")) { args->mode = 0; } else { args->mode = atoi(value); if (args->mode < 0) args->mode = 0; } dbg_printf(1, "Got %s for mode\n", args->mode?"VMChannel":"serial"); } return errors; } static int serial_init(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv) { serial_info *info; int ret; info = malloc(sizeof(*info)); if (!info) return -1; memset(info, 0, sizeof(*info)); info->priv = priv; info->cb = cb; ret = serial_config(config, info); if (ret < 0) { perror("serial_config"); return -1; } else if (ret > 0) { printf("%d errors found during configuration\n",ret); return -1; } info->maps = map; info->magic = SERIAL_PLUG_MAGIC; info->history = history_init(check_history, 10, sizeof(fence_req_t)); *c = (listener_context_t)info; start_event_listener(info->uri, info->path, info->mode, &info->wake_fd); sleep(1); return 0; } static int serial_shutdown(listener_context_t c) { serial_info *info = (serial_info *)c; dbg_printf(3, "Shutting down serial\n"); VALIDATE(info); info->magic = 0; stop_event_listener(); domain_sock_cleanup(); history_wipe(info->history); free(info->history); free(info->uri); free(info->path); free(info); return 0; } static listener_plugin_t serial_plugin = { .name = NAME, .version = SERIAL_VERSION, .init = serial_init, .dispatch = serial_dispatch, .cleanup = serial_shutdown, }; #ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &serial_plugin; } #else static void __attribute__((constructor)) serial_register_plugin(void) { plugin_reg_listener(&serial_plugin); } #endif fence-virt-1.0.0/server/serial.h000066400000000000000000000010721356743026700165250ustar00rootroot00000000000000#ifndef __VIRT_SERIAL_H #define __VIRT_SERIAL_H #include /* virt-sockets.c */ int domain_sock_setup(const char *domain, const char *socket_path); int domain_sock_close(const char *domain); int domain_sock_fdset(fd_set *set, int *max); /* Find the domain name associated with a FD */ int domain_sock_name(int fd, char *outbuf, size_t buflen); int domain_sock_cleanup(void); /* virt-serial.c - event thread control functions */ int start_event_listener(const char *uri, const char *path, int mode, int *wake_fd); int stop_event_listener(void); #endif fence-virt-1.0.0/server/static_map.c000066400000000000000000000077311356743026700173750ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include "simpleconfig.h" #include "static_map.h" #include "list.h" #include "debug.h" #include "serial.h" #include "uuid-test.h" struct perm_entry { list_head(); char name[128]; }; struct perm_group { list_head(); struct perm_entry *uuids; struct perm_entry *ips; char name[128]; }; static void static_map_cleanup(void **info) { struct perm_group *groups = (struct perm_group *)(*info); struct perm_group *group; struct perm_entry *entry; while (groups) { group = groups; list_remove(&groups, group); while (group->uuids) { entry = group->uuids; list_remove(&group->uuids, entry); free(entry); } while (group->ips) { entry = group->ips; list_remove(&group->ips, entry); free(entry); } free(group); } *info = NULL; } static int static_map_check(void *info, const char *value1, const char *value2) { struct perm_group *groups = (struct perm_group *)info; struct perm_group *group; struct perm_entry *left, *tmp; int x, y, uuid = 0; if (!info) return 1; /* no maps == wide open */ uuid = is_uuid(value1); list_for(&groups, group, x) { left = NULL; if (uuid) { list_for(&group->uuids, tmp, y) { if (!strcasecmp(tmp->name, value1)) { left = tmp; break; } } } else { list_for(&group->ips, tmp, y) { if (!strcasecmp(tmp->name, value1)) { left = tmp; break; } } } if (!left) continue; list_for(&group->uuids, tmp, y) { if (!strcasecmp(tmp->name, value2)) { return 1; } } } return 0; } static int static_map_load(void *config_ptr, void **perm_info) { config_object_t *config = config_ptr; int group_idx = 0; int entry_idx = 0; int found; char value[128]; char buf[256]; char buf2[512]; struct perm_group *group = NULL, *groups = NULL; struct perm_entry *entry = NULL; if (!perm_info) return -1; do { snprintf(buf, sizeof(buf)-1, "groups/group[%d]", ++group_idx); if (sc_get(config, buf, value, sizeof(value)) != 0) { snprintf(buf2, sizeof(buf2)-1, "%s/@uuid", buf); if (sc_get(config, buf2, value, sizeof(value)) != 0) { snprintf(buf2, sizeof(buf2)-1, "%s/@ip", buf); if (sc_get(config, buf2, value, sizeof(value)) != 0) { break; } } snprintf(value, sizeof(value), "unnamed-%d", group_idx); } group = malloc(sizeof(*group)); assert(group); memset(group, 0, sizeof(*group)); strncpy(group->name, value, sizeof(group->name)); dbg_printf(3, "Group: %s\n", value); found = 0; entry_idx = 0; do { snprintf(buf2, sizeof(buf2)-1, "%s/@uuid[%d]", buf, ++entry_idx); if (sc_get(config, buf2, value, sizeof(value)) != 0) { break; } ++found; entry = malloc(sizeof(*entry)); assert(entry); memset(entry, 0, sizeof(*entry)); strncpy(entry->name, value, sizeof(entry->name)); dbg_printf(3, " - UUID Entry: %s\n", value); list_insert(&group->uuids, entry); } while (1); entry_idx = 0; do { snprintf(buf2, sizeof(buf2)-1, "%s/@ip[%d]", buf, ++entry_idx); if (sc_get(config, buf2, value, sizeof(value)) != 0) { break; } ++found; entry = malloc(sizeof(*entry)); assert(entry); memset(entry, 0, sizeof(*entry)); strncpy(entry->name, value, sizeof(entry->name)); dbg_printf(3, " - IP Entry: %s\n", value); list_insert(&group->ips, entry); } while (1); if (!found) free(group); else list_insert(&groups, group); } while (1); *perm_info = groups; return 0; } static const map_object_t static_map_obj = { .load = static_map_load, .check = static_map_check, .cleanup = static_map_cleanup, .info = NULL }; void * map_init(void) { map_object_t *o; o = malloc(sizeof(*o)); if (!o) return NULL; memset(o, 0, sizeof(*o)); memcpy(o, &static_map_obj, sizeof(*o)); return (void *)o; } void map_release(void *c) { map_object_t *o = (map_object_t *)c; static_map_cleanup(&o->info); free(c); } fence-virt-1.0.0/server/tcp.c000066400000000000000000000300411356743026700160250ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2012 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include /* Local includes */ #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "mcast.h" #include "tcp.h" #include "tcp_listener.h" #include "debug.h" #include "fdops.h" #include "list.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "history.h" #define NAME "tcp" #define TCP_VERSION "0.1" #define TCP_MAGIC 0xc3dff7a9 #define VALIDATE(info) \ do {\ if (!info || info->magic != TCP_MAGIC)\ return -EINVAL;\ } while(0) typedef struct _tcp_options { char *key_file; char *addr; int family; unsigned int port; unsigned int hash; unsigned int auth; unsigned int flags; } tcp_options; typedef struct _tcp_info { uint64_t magic; void *priv; map_object_t *map; history_info_t *history; char key[MAX_KEY_LEN]; tcp_options args; const fence_callbacks_t *cb; ssize_t key_len; int listen_sock; } tcp_info; struct tcp_hostlist_arg { map_object_t *map; const char *src; int fd; }; /* * See if we fenced this node recently (successfully) * If so, ignore the request for a few seconds. * * We purge our history when the entries time out. */ static int check_history(void *a, void *b) { fence_req_t *old = a, *current = b; if (old->request == current->request && old->seqno == current->seqno && !strcasecmp((const char *)old->domain, (const char *)current->domain)) { return 1; } return 0; } static int tcp_hostlist(const char *vm_name, const char *vm_uuid, int state, void *priv) { struct tcp_hostlist_arg *arg = (struct tcp_hostlist_arg *)priv; host_state_t hinfo; struct timeval tv; int ret; if (map_check(arg->map, arg->src, vm_uuid) == 0) { /* if we don't have access to fence this VM, * we should not see it in a hostlist either */ return 0; } strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1); strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1); hinfo.state = state; tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int tcp_hostlist_begin(int fd) { struct timeval tv; char val = (char)RESP_HOSTLIST; tv.tv_sec = 1; tv.tv_usec = 0; return _write_retry(fd, &val, 1, &tv); } static int tcp_hostlist_end(int fd) { host_state_t hinfo; struct timeval tv; int ret; printf("Sending terminator packet\n"); memset(&hinfo, 0, sizeof(hinfo)); tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int do_fence_request_tcp(int fd, fence_req_t *req, tcp_info *info) { char ip_addr_src[1024]; char response = 1; struct tcp_hostlist_arg arg; int ret; /* Noops if auth == AUTH_NONE */ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) { printf("Failed to respond to challenge\n"); close(fd); return -1; } ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10); if (ret <= 0) { printf("Remote failed challenge\n"); close(fd); return -1; } dbg_printf(2, "Request %d seqno %d target %s\n", req->request, req->seqno, req->domain); switch(req->request) { case FENCE_NULL: response = info->cb->null((char *)req->domain, info->priv); break; case FENCE_ON: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->on((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_OFF: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->off((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_REBOOT: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->reboot((char *)req->domain, ip_addr_src, req->seqno, info->priv); break; case FENCE_STATUS: if (map_check(info->map, ip_addr_src, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->status((char *)req->domain, info->priv); break; case FENCE_DEVSTATUS: response = info->cb->devstatus(info->priv); break; case FENCE_HOSTLIST: arg.map = info->map; arg.src = ip_addr_src; arg.fd = fd; tcp_hostlist_begin(arg.fd); response = info->cb->hostlist(tcp_hostlist, &arg, info->priv); tcp_hostlist_end(arg.fd); break; } dbg_printf(3, "Sending response to caller...\n"); if (_write_retry(fd, &response, 1, NULL) < 0) { perror("write"); } history_record(info->history, req); if (fd != -1) close(fd); return 1; } static int tcp_dispatch(listener_context_t c, struct timeval *timeout) { tcp_info *info; fence_req_t data; fd_set rfds; int n; int client_fd; int ret; struct timeval tv; if (timeout != NULL) memcpy(&tv, timeout, sizeof(tv)); else { tv.tv_sec = 1; tv.tv_usec = 0; } info = (tcp_info *)c; VALIDATE(info); FD_ZERO(&rfds); FD_SET(info->listen_sock, &rfds); n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout); if (n <= 0) { if (errno == EINTR || errno == EAGAIN) n = 0; else dbg_printf(2, "select: %s\n", strerror(errno)); return n; } client_fd = accept(info->listen_sock, NULL, NULL); if (client_fd < 0) { perror("accept"); return -1; } dbg_printf(3, "Accepted client...\n"); ret = _read_retry(client_fd, &data, sizeof(data), &tv); if (ret != sizeof(data)) { dbg_printf(3, "Invalid request (read %d bytes)\n", ret); close(client_fd); return 0; } swab_fence_req_t(&data); if (!verify_request(&data, info->args.hash, info->key, info->key_len)) { printf("Key mismatch; dropping client\n"); close(client_fd); return 0; } dbg_printf(3, "Request %d seqno %d domain %s\n", data.request, data.seqno, data.domain); if (history_check(info->history, &data) == 1) { printf("We just did this request; dropping client\n"); close(client_fd); return 0; } switch(info->args.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: printf("Plain TCP request\n"); do_fence_request_tcp(client_fd, &data, info); break; default: printf("XXX Unhandled authentication\n"); } return 0; } static int tcp_config(config_object_t *config, tcp_options *args) { char value[1024]; int errors = 0; #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); #endif if (sc_get(config, "listeners/tcp/@key_file", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for key_file\n", value); args->key_file = strdup(value); } else { args->key_file = strdup(DEFAULT_KEY_FILE); if (!args->key_file) { dbg_printf(1, "Failed to allocate memory\n"); return -1; } } args->hash = DEFAULT_HASH; if (sc_get(config, "listeners/tcp/@hash", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for hash\n", value); if (!strcasecmp(value, "none")) { args->hash = HASH_NONE; } else if (!strcasecmp(value, "sha1")) { args->hash = HASH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->hash = HASH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->hash = HASH_SHA512; } else { dbg_printf(1, "Unsupported hash: %s\n", value); ++errors; } } args->auth = DEFAULT_AUTH; if (sc_get(config, "listeners/tcp/@auth", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for auth\n", value); if (!strcasecmp(value, "none")) { args->hash = AUTH_NONE; } else if (!strcasecmp(value, "sha1")) { args->hash = AUTH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->hash = AUTH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->hash = AUTH_SHA512; } else { dbg_printf(1, "Unsupported auth: %s\n", value); ++errors; } } args->family = PF_INET; if (sc_get(config, "listeners/tcp/@family", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for family\n", value); if (!strcasecmp(value, "ipv4")) { args->family = PF_INET; } else if (!strcasecmp(value, "ipv6")) { args->family = PF_INET6; } else { dbg_printf(1, "Unsupported family: %s\n", value); ++errors; } } if (sc_get(config, "listeners/tcp/@address", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for address\n", value); args->addr = strdup(value); } else { if (args->family == PF_INET) { args->addr = strdup(IPV4_TCP_ADDR_DEFAULT); } else { args->addr = strdup(IPV6_TCP_ADDR_DEFAULT); } } if (!args->addr) { return -1; } args->port = DEFAULT_MCAST_PORT; if (sc_get(config, "listeners/tcp/@port", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for port\n", value); args->port = atoi(value); if (args->port <= 0) { dbg_printf(1, "Invalid port: %s\n", value); ++errors; } } return errors; } static int tcp_init(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv) { tcp_info *info; int listen_sock, ret; /* Initialize NSS; required to do hashing, as silly as that sounds... */ if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } info = calloc(1, sizeof(*info)); if (!info) return -1; info->priv = priv; info->cb = cb; info->map = map; ret = tcp_config(config, &info->args); if (ret < 0) perror("tcp_config"); else if (ret > 0) printf("%d errors found during configuration\n",ret); if (ret != 0) { if (info->args.key_file) free(info->args.key_file); if (info->args.addr) free(info->args.addr); free(info); return -1; } if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) { info->key_len = read_key_file(info->args.key_file, info->key, sizeof(info->key)); if (info->key_len < 0) { printf("Could not read %s; operating without " "authentication\n", info->args.key_file); info->args.auth = AUTH_NONE; info->args.hash = HASH_NONE; info->key_len = 0; } } if (info->args.family == PF_INET) { listen_sock = ipv4_listen(info->args.addr, info->args.port, 10); } else { listen_sock = ipv6_listen(info->args.addr, info->args.port, 10); } if (listen_sock < 0) { printf("Could not set up listen socket\n"); if (info->args.key_file) free(info->args.key_file); if (info->args.addr) free(info->args.addr); free(info); return -1; } info->magic = TCP_MAGIC; info->listen_sock = listen_sock; info->history = history_init(check_history, 10, sizeof(fence_req_t)); *c = (listener_context_t)info; return 0; } static int tcp_shutdown(listener_context_t c) { tcp_info *info = (tcp_info *)c; VALIDATE(info); info->magic = 0; history_wipe(info->history); free(info->history); free(info->args.key_file); free(info->args.addr); close(info->listen_sock); free(info); return 0; } static listener_plugin_t tcp_plugin = { .name = NAME, .version = TCP_VERSION, .init = tcp_init, .dispatch = tcp_dispatch, .cleanup = tcp_shutdown, }; #ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &tcp_plugin; } #else static void __attribute__((constructor)) tcp_register_plugin(void) { plugin_reg_listener(&tcp_plugin); } #endif fence-virt-1.0.0/server/uuid-test.c000066400000000000000000000015121356743026700171630ustar00rootroot00000000000000#include "config.h" #include #include #include int is_uuid(const char *value) { uuid_t id; char test_value[37]; if (strlen(value) < 36) { return 0; } if (uuid_is_null(id) < 0) { errno = EINVAL; return -1; } if (uuid_parse(value, id) < 0) { return 0; } memset(test_value, 0, sizeof(test_value)); uuid_unparse(id, test_value); if (strcasecmp(value, test_value)) { return 0; } return 1; } #ifdef STANDALONE #include int main(int argc, char **argv) { int ret; if (argc < 2) { printf("Usage: uuidtest \n"); return 1; } ret = is_uuid(argv[1]); if (ret == 0) { printf("%s is NOT a uuid\n", argv[1]); } else if (ret == 1) { printf("%s is a uuid\n", argv[1]); } else { printf("Error: %s\n", strerror(errno)); return 1; } return 0; } #endif fence-virt-1.0.0/server/uuid-test.h000066400000000000000000000002321356743026700171660ustar00rootroot00000000000000#ifndef __UUID_TEST_H #define __UUID_TEST_H #ifdef __cplusplus extern "C" { #endif int is_uuid(const char *value); #ifdef __cplusplus } #endif #endif fence-virt-1.0.0/server/virt-serial.c000066400000000000000000000215631356743026700175110ustar00rootroot00000000000000// #include #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "simpleconfig.h" #include "debug.h" #define DEBUG0(fmt) dbg_printf(5,"%s:%d :: " fmt "\n", \ __func__, __LINE__) #define DEBUG1(fmt, ...) dbg_printf(5, "%s:%d: " fmt "\n", \ __func__, __LINE__, __VA_ARGS__) #include "serial.h" #define STREQ(a,b) (strcmp((a),(b)) == 0) static pthread_t event_tid = 0; static int run = 0; /* Prototypes */ const char *eventToString(int event); int myDomainEventCallback1(virConnectPtr conn, virDomainPtr dom, int event, int detail, void *opaque); void usage(const char *pname); struct domain_info { virDomainPtr dom; virDomainEventType event; }; static int is_in_directory(const char *dir, const char *pathspec) { char *last_slash = NULL; size_t dirlen, pathlen; if (!dir || !pathspec) return 0; dirlen = strlen(dir); pathlen = strlen(pathspec); /* printf("dirlen = %d pathlen = %d\n", dirlen, pathlen); */ /* chop off trailing slashes */ while (dirlen && dir[dirlen-1]=='/') --dirlen; /* chop off leading slashes */ while (dirlen && dir[0] == '/') { ++dir; --dirlen; } /* chop off leading slashes */ while (pathlen && pathspec[0] == '/') { ++pathspec; --pathlen; } if (!dirlen || !pathlen) return 0; if (pathlen <= dirlen) return 0; last_slash = strrchr(pathspec, '/'); if (!last_slash) return 0; while (*last_slash == '/' && last_slash > pathspec) --last_slash; if (last_slash == pathspec) return 0; pathlen = last_slash - pathspec + 1; /*printf("real dirlen = %d real pathlen = %d\n", dirlen, pathlen);*/ if (pathlen != dirlen) return 0; /* todo - intelligently skip multiple slashes mid-path */ return !strncmp(dir, pathspec, dirlen); } static int domainStarted(virDomainPtr mojaDomain, const char *path, int mode) { char dom_uuid[42]; char *xml; xmlDocPtr doc; xmlNodePtr cur, devices, child, serial; xmlAttrPtr attr, attr_mode, attr_path; if (!mojaDomain) return -1; virDomainGetUUIDString(mojaDomain, dom_uuid); xml = virDomainGetXMLDesc(mojaDomain, 0); // printf("%s\n", xml); // @todo: free mojaDomain // parseXML output doc = xmlParseMemory(xml, strlen(xml)); xmlFree(xml); cur = xmlDocGetRootElement(doc); if (cur == NULL) { fprintf(stderr, "Empty doc\n"); xmlFreeDoc(doc); return -1; } if (xmlStrcmp(cur->name, (const xmlChar *) "domain")) { fprintf(stderr, "no domain?\n"); xmlFreeDoc(doc); return -1; } devices = cur->xmlChildrenNode; for (devices = cur->xmlChildrenNode; devices != NULL; devices = devices->next) { if (xmlStrcmp(devices->name, (const xmlChar *) "devices")) { continue; } for (child = devices->xmlChildrenNode; child != NULL; child = child->next) { if ((!mode && xmlStrcmp(child->name, (const xmlChar *) "serial")) || (mode && xmlStrcmp(child->name, (const xmlChar *) "channel"))) { continue; } attr = xmlHasProp(child, (const xmlChar *)"type"); if (attr == NULL) continue; if (xmlStrcmp(attr->children->content, (const xmlChar *) "unix")) { continue; } for (serial = child->xmlChildrenNode; serial != NULL; serial = serial->next) { if (xmlStrcmp(serial->name, (const xmlChar *) "source")) { continue; } attr_mode = xmlHasProp(serial, (const xmlChar *)"mode"); attr_path = xmlHasProp(serial, (const xmlChar *)"path"); if (!attr_path || !attr_mode) continue; if (xmlStrcmp(attr_mode->children->content, (const xmlChar *) "bind")) continue; if (path && !is_in_directory(path, (const char *) attr_path->children->content)) continue; domain_sock_setup(dom_uuid, (const char *) attr_path->children->content); } } } xmlFreeDoc(doc); return 0; } static int registerExisting(virConnectPtr vp, const char *path, int mode) { int *d_ids = NULL; int d_count, x; virDomainPtr dom; virDomainInfo d_info; errno = EINVAL; if (!vp) return -1; d_count = virConnectNumOfDomains(vp); if (d_count <= 0) { if (d_count == 0) { /* Successful, but no domains running */ errno = 0; return 0; } goto out_fail; } d_ids = malloc(sizeof (int) * d_count); if (!d_ids) goto out_fail; if (virConnectListDomains(vp, d_ids, d_count) < 0) goto out_fail; /* Ok, we have the domain IDs - let's get their names and states */ for (x = 0; x < d_count; x++) { dom = virDomainLookupByID(vp, d_ids[x]); if (!dom) { /* XXX doom */ goto out_fail; } if (virDomainGetInfo(dom, &d_info) < 0) { /* XXX no info for the domain?!! */ virDomainFree(dom); goto out_fail; } if (d_info.state != VIR_DOMAIN_SHUTOFF && d_info.state != VIR_DOMAIN_CRASHED) domainStarted(dom, path, mode); virDomainFree(dom); } out_fail: free(d_ids); return 0; } static int domainStopped(virDomainPtr mojaDomain) { char dom_uuid[42]; if (!mojaDomain) return -1; virDomainGetUUIDString(mojaDomain, dom_uuid); domain_sock_close(dom_uuid); return 0; } struct event_args { char *uri; char *path; int mode; int wake_fd; }; void connectClose(virConnectPtr conn ATTRIBUTE_UNUSED, int reason, void *opaque ATTRIBUTE_UNUSED) { switch (reason) { case VIR_CONNECT_CLOSE_REASON_ERROR: dbg_printf(2, "Connection closed due to I/O error\n"); break; case VIR_CONNECT_CLOSE_REASON_EOF: dbg_printf(2, "Connection closed due to end of file\n"); break; case VIR_CONNECT_CLOSE_REASON_KEEPALIVE: dbg_printf(2, "Connection closed due to keepalive timeout\n"); break; case VIR_CONNECT_CLOSE_REASON_CLIENT: dbg_printf(2, "Connection closed due to client request\n"); break; default: dbg_printf(2, "Connection closed due to unknown reason\n"); break; }; run = 0; } int myDomainEventCallback1(virConnectPtr conn, virDomainPtr dom, int event, int detail, void *opaque) { struct event_args *args = (struct event_args *)opaque; if (event == VIR_DOMAIN_EVENT_STARTED || event == VIR_DOMAIN_EVENT_STOPPED) { virDomainRef(dom); if (event == VIR_DOMAIN_EVENT_STARTED) { domainStarted(dom, args->path, args->mode); virDomainFree(dom); if (write(args->wake_fd, "x", 1) != 1) { dbg_printf(3, "Unable to wake up thread\n"); } } else if (event == VIR_DOMAIN_EVENT_STOPPED) { domainStopped(dom); virDomainFree(dom); } } return 0; } static void * event_thread(void *arg) { struct event_args *args = (struct event_args *)arg; virConnectPtr dconn = NULL; int callback1ret = -1; dbg_printf(3, "Libvirt event listener starting\n"); if (args->uri) dbg_printf(3," * URI: %s\n", args->uri); if (args->path) dbg_printf(3," * Socket path: %s\n", args->path); dbg_printf(3," * Mode: %s\n", args->mode ? "VMChannel" : "Serial"); if (virEventRegisterDefaultImpl() < 0) { dbg_printf(1, "Failed to register default event impl\n"); goto out; } dconn = virConnectOpen(args->uri); if (!dconn) { dbg_printf(1, "Error connecting to libvirt\n"); goto out; } virConnectRegisterCloseCallback(dconn, connectClose, NULL, NULL); DEBUG0("Registering domain event cbs"); registerExisting(dconn, args->path, args->mode); callback1ret = virConnectDomainEventRegister(dconn, myDomainEventCallback1, arg, NULL); if (callback1ret != -1) { if (virConnectSetKeepAlive(dconn, 5, 5) < 0) { dbg_printf(1, "Failed to start keepalive protocol\n"); run = 0; } while (run) { if (virEventRunDefaultImpl() < 0) { dbg_printf(1, "RunDefaultImpl Failed\n"); } } DEBUG0("Deregistering event handlers"); virConnectDomainEventDeregister(dconn, myDomainEventCallback1); } DEBUG0("Closing connection"); if (dconn && virConnectClose(dconn) < 0) { dbg_printf(1, "error closing libvirt connection\n"); } out: free(args->uri); free(args->path); free(args); return NULL; } int start_event_listener(const char *uri, const char *path, int mode, int *wake_fd) { struct event_args *args = NULL; int wake_pipe[2]; virInitialize(); args = malloc(sizeof(*args)); if (!args) return -1; memset(args, 0, sizeof(*args)); if (pipe2(wake_pipe, O_CLOEXEC) < 0) { goto out_fail; } if (uri) { args->uri = strdup(uri); if (args->uri == NULL) goto out_fail; } if (path) { args->path = strdup(path); if (args->path == NULL) goto out_fail; } args->mode = mode; //args->p_tid = pthread_self(); *wake_fd = wake_pipe[0]; args->wake_fd = wake_pipe[1]; run = 1; return pthread_create(&event_tid, NULL, event_thread, args); out_fail: free(args->uri); free(args->path); free(args); return -1; } int stop_event_listener(void) { run = 0; //pthread_cancel(event_tid); pthread_join(event_tid, NULL); event_tid = 0; return 0; } fence-virt-1.0.0/server/virt-sockets.c000066400000000000000000000101201356743026700176700ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include #include #include #include "serial.h" #include "debug.h" #include "simpleconfig.h" struct socket_list { list_head(); char *domain_name; char *socket_path; int socket_fd; }; static struct socket_list *socks = NULL; static pthread_mutex_t sock_list_mutex = PTHREAD_MUTEX_INITIALIZER; static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout) { int ret, flags, err; unsigned l; fd_set rfds, wfds; struct timeval tv; /* Set up non-blocking connect */ flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); ret = connect(fd, dest, len); if ((ret < 0) && (errno != EINPROGRESS)) return -1; if (ret == 0) goto done; FD_ZERO(&rfds); FD_SET(fd, &rfds); FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = timeout; tv.tv_usec = 0; if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) { errno = ETIMEDOUT; return -1; } if (!FD_ISSET(fd, &rfds) && !FD_ISSET(fd, &wfds)) { errno = EIO; return -1; } l = sizeof(err); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &l) < 0) { return -1; } if (err != 0) { errno = err; return -1; } done: fcntl(fd, F_SETFL, flags); return 0; } int domain_sock_setup(const char *domain, const char *socket_path) { struct sockaddr_un *sun = NULL; struct socket_list *node = NULL; socklen_t sun_len; int sock = -1; sun_len = sizeof(*sun) + strlen(socket_path) + 1; sun = malloc(sun_len); if (!sun) return -1; memset((char *)sun, 0, sun_len); sun->sun_family = PF_LOCAL; strncpy(sun->sun_path, socket_path, sizeof(sun->sun_path) - 1); sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock < 0) goto out_fail; if (connect_nb(sock, (struct sockaddr *)sun, SUN_LEN(sun), 3) < 0) goto out_fail; free(sun); sun = NULL; node = calloc(1, sizeof(*node)); if (!node) goto out_fail; node->domain_name = strdup(domain); if (!node->domain_name) goto out_fail; node->socket_path = strdup(socket_path); if (!node->socket_path) goto out_fail; node->socket_fd = sock; pthread_mutex_lock(&sock_list_mutex); list_insert(&socks, node); pthread_mutex_unlock(&sock_list_mutex); dbg_printf(3, "Registered %s on %d\n", domain, sock); return 0; out_fail: if (node) { free(node->domain_name); if (node->socket_path) free(node->socket_path); free(node); } free(sun); if (sock >= 0) close(sock); return -1; } int domain_sock_close(const char *domain) { struct socket_list *node = NULL; struct socket_list *dead = NULL; int x; pthread_mutex_lock(&sock_list_mutex); list_for(&socks, node, x) { if (!strcasecmp(domain, node->domain_name)) { list_remove(&socks, node); dead = node; break; } } pthread_mutex_unlock(&sock_list_mutex); if (dead) { dbg_printf(3, "Unregistered %s, fd%d\n", dead->domain_name, dead->socket_fd); close(dead->socket_fd); free(dead->domain_name); free(dead->socket_path); free(dead); } return 0; } int domain_sock_fdset(fd_set *fds, int *max) { struct socket_list *node = NULL; int x = 0, _max = -1; pthread_mutex_lock(&sock_list_mutex); list_for(&socks, node, x) { FD_SET(node->socket_fd, fds); if (node->socket_fd > _max) _max = node->socket_fd; } pthread_mutex_unlock(&sock_list_mutex); if (max) *max = _max; return x; } int domain_sock_name(int fd, char *outbuf, size_t buflen) { struct socket_list *node = NULL; int ret = 1, x = 0; pthread_mutex_lock(&sock_list_mutex); list_for(&socks, node, x) { if (node->socket_fd == fd) { snprintf(outbuf, buflen, "%s", node->domain_name); ret = 0; break; } } pthread_mutex_unlock(&sock_list_mutex); return ret; } int domain_sock_cleanup(void) { struct socket_list *dead= NULL; pthread_mutex_lock(&sock_list_mutex); while(socks) { dead = socks; list_remove(&socks, dead); close(dead->socket_fd); free(dead->domain_name); free(dead->socket_path); free(dead); } pthread_mutex_unlock(&sock_list_mutex); return 0; } fence-virt-1.0.0/server/virt.c000066400000000000000000000327111356743026700162310ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "uuid-test.h" #include "virt.h" static int _compare_virt(const void *_left, const void *_right) { virt_state_t *left = (virt_state_t *)_left, *right = (virt_state_t *)_right; return strcasecmp(left->v_name, right->v_name); } static void _free_dom_list(virDomainPtr *dom_list, int len) { int x; if (!dom_list || len <= 0) return; for (x = 0 ; x < len; x++) virDomainFree(dom_list[x]); free(dom_list); } virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id) { virt_list_t *vl = NULL; int d_count = 0; int i; errno = EINVAL; if (!vp || vp_count < 1) return NULL; for (i = 0 ; i < vp_count ; i++) { int x; virDomainPtr *dom_list; virt_list_t *new_vl; int ret = virConnectListAllDomains(vp[i], &dom_list, 0); if (ret == 0) continue; if (ret < 0) { int saved_errno = errno; dbg_printf(2, "Error: virConnectListAllDomains: %d %d\n", ret, saved_errno); if (vl) free(vl); errno = saved_errno; return NULL; } d_count += ret; new_vl = realloc(vl, sizeof(uint32_t) + sizeof(virt_state_t) * d_count); if (!new_vl) { _free_dom_list(dom_list, ret); free(vl); return NULL; } vl = new_vl; vl->vm_count = d_count; /* Ok, we have the domain IDs - let's get their names and states */ for (x = 0; x < ret; x++) { char *d_name; virDomainInfo d_info; char d_uuid[MAX_DOMAINNAME_LENGTH]; virDomainPtr dom = dom_list[x]; if (!(d_name = (char *)virDomainGetName(dom))) { _free_dom_list(dom_list, ret); free(vl); return NULL; } if (virDomainGetUUIDString(dom, d_uuid) != 0) { _free_dom_list(dom_list, ret); free(vl); return NULL; } if (virDomainGetInfo(dom, &d_info) < 0) { _free_dom_list(dom_list, ret); free(vl); return NULL; } /* Store the name & state */ strncpy(vl->vm_states[x].v_name, d_name, MAX_DOMAINNAME_LENGTH); strncpy(vl->vm_states[x].v_uuid, d_uuid, MAX_DOMAINNAME_LENGTH); vl->vm_states[x].v_state.s_state = d_info.state; vl->vm_states[x].v_state.s_owner = my_id; } _free_dom_list(dom_list, ret); } /* No domains found */ if (!vl) return NULL; /* We have all the locally running domains & states now */ /* Sort */ qsort(&vl->vm_states[0], vl->vm_count, sizeof(vl->vm_states[0]), _compare_virt); return vl; } int vl_add(virt_list_t **vl, virt_state_t *vm) { virt_list_t *new_vl; size_t oldlen; size_t newlen; if (!vl) return -1; if (!*vl) { *vl = malloc(sizeof(uint32_t) + sizeof(virt_state_t)); if (!*vl) return -1; (*vl)->vm_count = 1; memcpy(&(*vl)->vm_states[0], vm, sizeof(virt_state_t)); return 0; } oldlen = sizeof(uint32_t) + sizeof(virt_state_t) * (*vl)->vm_count; newlen = oldlen + sizeof(virt_state_t); new_vl = malloc(newlen); if (!new_vl) return -1; memcpy(new_vl, *vl, oldlen); memcpy(&new_vl->vm_states[(*vl)->vm_count], vm, sizeof(virt_state_t)); new_vl->vm_count++; free(*vl); *vl = new_vl; return 0; } int vl_remove_by_owner(virt_list_t **vl, uint32_t owner) { int i; int removed = 0; virt_list_t *new_vl; if (!vl || !*vl) return 0; for (i = 0 ; i < (*vl)->vm_count ; i++) { if ((*vl)->vm_states[i].v_state.s_owner == owner) { dbg_printf(2, "Removing %s\n", (*vl)->vm_states[i].v_name); memset(&(*vl)->vm_states[i].v_state, 0, sizeof((*vl)->vm_states[i].v_state)); (*vl)->vm_states[i].v_name[0] = 0xff; (*vl)->vm_states[i].v_uuid[0] = 0xff; removed++; } } if (!removed) return 0; qsort(&(*vl)->vm_states[0], (*vl)->vm_count, sizeof((*vl)->vm_states[0]), _compare_virt); (*vl)->vm_count -= removed; new_vl = realloc(*vl, sizeof(uint32_t) + (sizeof(virt_state_t) * ((*vl)->vm_count))); if (new_vl) *vl = new_vl; return removed; } int vl_update(virt_list_t **vl, virt_state_t *vm) { virt_state_t *v = NULL; if (!vl) return -1; if (!*vl) return vl_add(vl, vm); if (strlen(vm->v_uuid) > 0) v = vl_find_uuid(*vl, vm->v_uuid); if (v == NULL && strlen(vm->v_name) > 0) v = vl_find_name(*vl, vm->v_name); if (v == NULL) { dbg_printf(2, "Adding new entry for VM %s\n", vm->v_name); vl_add(vl, vm); } else { dbg_printf(2, "Updating entry for VM %s\n", vm->v_name); memcpy(&v->v_state, &vm->v_state, sizeof(v->v_state)); } return 0; } void vl_print(virt_list_t *vl) { int x; printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "Domain", "UUID", "Owner", "State"); printf("%-24.24s %-36.36s %-5.5s %-5.5s\n", "------", "----", "-----", "-----"); if (!vl || !vl->vm_count) return; for (x = 0; x < vl->vm_count; x++) { printf("%-24.24s %-36.36s %-5.5d %-5.5d\n", vl->vm_states[x].v_name, vl->vm_states[x].v_uuid, vl->vm_states[x].v_state.s_owner, vl->vm_states[x].v_state.s_state); } } virt_state_t * vl_find_name(virt_list_t *vl, const char *name) { int x; if (!vl || !name || !vl->vm_count) return NULL; for (x = 0; x < vl->vm_count; x++) { if (!strcasecmp(vl->vm_states[x].v_name, name)) return &vl->vm_states[x]; } return NULL; } virt_state_t * vl_find_uuid(virt_list_t *vl, const char *uuid) { int x; if (!vl || !uuid || !vl->vm_count) return NULL; for (x = 0; x < vl->vm_count; x++) { if (!strcasecmp(vl->vm_states[x].v_uuid, uuid)) return &vl->vm_states[x]; } return NULL; } void vl_free(virt_list_t *old) { free(old); } static inline int wait_domain(const char *vm_name, virConnectPtr vp, int timeout) { int tries = 0; int response = 1; int ret; virDomainPtr vdp; virDomainInfo vdi; int uuid_check; uuid_check = is_uuid(vm_name); if (uuid_check) { vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name); } else { vdp = virDomainLookupByName(vp, vm_name); } if (!vdp) return 0; /* Check domain liveliness. If the domain is still here, we return failure, and the client must then retry */ /* XXX On the xen 3.0.4 API, we will be able to guarantee synchronous virDomainDestroy, so this check will not be necessary */ do { if (++tries > timeout) break; sleep(1); if (uuid_check) { vdp = virDomainLookupByUUIDString(vp, (const char *)vm_name); } else { vdp = virDomainLookupByName(vp, vm_name); } if (!vdp) { dbg_printf(2, "Domain no longer exists\n"); response = 0; break; } memset(&vdi, 0, sizeof(vdi)); ret = virDomainGetInfo(vdp, &vdi); virDomainFree(vdp); if (ret < 0) continue; if (vdi.state == VIR_DOMAIN_SHUTOFF) { dbg_printf(2, "Domain has been shut off\n"); response = 0; break; } dbg_printf(4, "Domain still exists (state %d) after %d seconds\n", vdi.state, tries); } while (1); return response; } int vm_off(virConnectPtr *vp, int vp_count, const char *vm_name) { virDomainPtr vdp = NULL; virDomainInfo vdi; virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *); int ret = -1; int i; if (is_uuid(vm_name)) virt_lookup_fn = virDomainLookupByUUIDString; else virt_lookup_fn = virDomainLookupByName; for (i = 0 ; i < vp_count ; i++) { vdp = virt_lookup_fn(vp[i], vm_name); if (vdp) break; } if (!vdp) { dbg_printf(2, "[virt:OFF] Domain %s does not exist\n", vm_name); return 1; } if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) { dbg_printf(2, "[virt:OFF] Nothing to do - " "domain %s is already off\n", vm_name); virDomainFree(vdp); return 0; } syslog(LOG_NOTICE, "Destroying domain %s\n", vm_name); dbg_printf(2, "[virt:OFF] Calling virDomainDestroy for %s\n", vm_name); ret = virDomainDestroy(vdp); virDomainFree(vdp); if (ret < 0) { syslog(LOG_NOTICE, "Failed to destroy domain %s: %d\n", vm_name, ret); dbg_printf(2, "[virt:OFF] Failed to destroy domain: %s %d\n", vm_name, ret); return 1; } if (ret) { syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n", vm_name); dbg_printf(2, "[virt:OFF] Domain %s still exists; fencing failed\n", vm_name); return 1; } dbg_printf(2, "[virt:OFF] Success for %s\n", vm_name); return 0; } int vm_on(virConnectPtr *vp, int vp_count, const char *vm_name) { virDomainPtr vdp = NULL; virDomainInfo vdi; virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *); int ret = -1; int i; if (is_uuid(vm_name)) virt_lookup_fn = virDomainLookupByUUIDString; else virt_lookup_fn = virDomainLookupByName; for (i = 0 ; i < vp_count ; i++) { vdp = virt_lookup_fn(vp[i], vm_name); if (vdp) break; } if (!vdp) { dbg_printf(2, "[virt:ON] Domain %s does not exist\n", vm_name); return 1; } if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state != VIR_DOMAIN_SHUTOFF) { dbg_printf(2, "Nothing to do - domain %s is already running\n", vm_name); virDomainFree(vdp); return 0; } syslog(LOG_NOTICE, "Starting domain %s\n", vm_name); dbg_printf(2, "[virt:ON] Calling virDomainCreate for %s\n", vm_name); ret = virDomainCreate(vdp); virDomainFree(vdp); if (ret < 0) { syslog(LOG_NOTICE, "Failed to start domain %s: %d\n", vm_name, ret); dbg_printf(2, "[virt:ON] virDomainCreate() failed for %s: %d\n", vm_name, ret); return 1; } if (ret) { syslog(LOG_NOTICE, "Domain %s did not start\n", vm_name); dbg_printf(2, "[virt:ON] Domain %s did not start\n", vm_name); return 1; } syslog(LOG_NOTICE, "Domain %s started\n", vm_name); dbg_printf(2, "[virt:ON] Success for %s\n", vm_name); return 0; } int vm_status(virConnectPtr *vp, int vp_count, const char *vm_name) { virDomainPtr vdp = NULL; virDomainInfo vdi; int ret = 0; int i; virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *); if (is_uuid(vm_name)) virt_lookup_fn = virDomainLookupByUUIDString; else virt_lookup_fn = virDomainLookupByName; for (i = 0 ; i < vp_count ; i++) { vdp = virt_lookup_fn(vp[i], vm_name); if (vdp) break; } if (!vdp) { dbg_printf(2, "[virt:STATUS] Unknown VM %s - return OFF\n", vm_name); return RESP_OFF; } if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) { dbg_printf(2, "[virt:STATUS] VM %s is OFF\n", vm_name); ret = RESP_OFF; } if (vdp) virDomainFree(vdp); return ret; } int vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name) { virDomainPtr vdp = NULL, nvdp; virDomainInfo vdi; char *domain_desc; virConnectPtr vcp = NULL; virDomainPtr (*virt_lookup_fn)(virConnectPtr, const char *); int ret; int i; if (is_uuid(vm_name)) virt_lookup_fn = virDomainLookupByUUIDString; else virt_lookup_fn = virDomainLookupByName; for (i = 0 ; i < vp_count ; i++) { vdp = virt_lookup_fn(vp[i], vm_name); if (vdp) { vcp = vp[i]; break; } } if (!vdp || !vcp) { dbg_printf(2, "[virt:REBOOT] Nothing to do - domain %s does not exist\n", vm_name); return 1; } if (virDomainGetInfo(vdp, &vdi) == 0 && vdi.state == VIR_DOMAIN_SHUTOFF) { dbg_printf(2, "[virt:REBOOT] Nothing to do - domain %s is off\n", vm_name); virDomainFree(vdp); return 0; } syslog(LOG_NOTICE, "Rebooting domain %s\n", vm_name); dbg_printf(5, "[virt:REBOOT] Rebooting domain %s...\n", vm_name); domain_desc = virDomainGetXMLDesc(vdp, 0); if (!domain_desc) { dbg_printf(5, "[virt:REBOOT] Failed getting domain description " "from libvirt for %s...\n", vm_name); } dbg_printf(2, "[virt:REBOOT] Calling virDomainDestroy(%p) for %s\n", vdp, vm_name); ret = virDomainDestroy(vdp); if (ret < 0) { dbg_printf(2, "[virt:REBOOT] virDomainDestroy() failed for %s: %d/%d\n", vm_name, ret, errno); if (domain_desc) free(domain_desc); virDomainFree(vdp); return 1; } ret = wait_domain(vm_name, vcp, 15); if (ret) { syslog(LOG_NOTICE, "Domain %s still exists; fencing failed\n", vm_name); dbg_printf(2, "[virt:REBOOT] Domain %s still exists; fencing failed\n", vm_name); if (domain_desc) free(domain_desc); virDomainFree(vdp); return 1; } if (!domain_desc) return 0; /* 'on' is not a failure */ ret = 0; dbg_printf(3, "[[ XML Domain Info ]]\n"); dbg_printf(3, "%s\n[[ XML END ]]\n", domain_desc); dbg_printf(2, "[virt:REBOOT] Calling virDomainCreateLinux() for %s\n", vm_name); nvdp = virDomainCreateLinux(vcp, domain_desc, 0); if (nvdp == NULL) { /* More recent versions of libvirt or perhaps the * KVM back-end do not let you create a domain from * XML if there is already a defined domain description * with the same name that it knows about. You must * then call virDomainCreate() */ dbg_printf(2, "[virt:REBOOT] virDomainCreateLinux() failed for %s; " "Trying virDomainCreate()\n", vm_name); if (virDomainCreate(vdp) < 0) { syslog(LOG_NOTICE, "Could not restart %s\n", vm_name); dbg_printf(1, "[virt:REBOOT] Failed to recreate guest %s!\n", vm_name); } } free(domain_desc); virDomainFree(vdp); return ret; } fence-virt-1.0.0/server/virt.h000066400000000000000000000035571356743026700162440ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2006-2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _VIRT_H #define _VIRT_H #include #include #include #include "xvm.h" typedef struct { uint32_t s_owner; int32_t s_state; } vm_state_t; typedef struct { char v_name[MAX_DOMAINNAME_LENGTH]; char v_uuid[MAX_DOMAINNAME_LENGTH]; vm_state_t v_state; } virt_state_t; /** This is stored in our private checkpoint section. */ typedef struct _virt_list { uint32_t vm_count; virt_state_t vm_states[0]; } virt_list_t; virt_list_t *vl_get(virConnectPtr *vp, int vp_count, int my_id); void vl_print(virt_list_t *vl); void vl_free(virt_list_t *old); virt_state_t *vl_find_uuid(virt_list_t *vl, const char *name); virt_state_t *vl_find_name(virt_list_t *vl, const char *name); int vl_add(virt_list_t **vl, virt_state_t *vm); int vl_update(virt_list_t **vl, virt_state_t *vm); int vl_remove_by_owner(virt_list_t **vl, uint32_t owner); int vm_off(virConnectPtr *vp, int vp_count, const char *vm_name); int vm_on(virConnectPtr *vp, int vp_count, const char *vm_name); int vm_status(virConnectPtr *vp, int vp_count, const char *vm_name); int vm_reboot(virConnectPtr *vp, int vp_count, const char *vm_name); #endif fence-virt-1.0.0/server/vsock.c000066400000000000000000000305651356743026700163770ustar00rootroot00000000000000/* Copyright Red Hat, Inc. 2017 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include /* Local includes */ #include "list.h" #include "simpleconfig.h" #include "static_map.h" #include "server_plugin.h" #include "history.h" #include "xvm.h" #include "simple_auth.h" #include "options.h" #include "mcast.h" #include "tcp.h" #include "tcp_listener.h" #include "debug.h" #include "fdops.h" #define NAME "vsock" #define VSOCK_VERSION "0.1" #define VSOCK_MAGIC 0xa32d27c1e #define VALIDATE(info) \ do {\ if (!info || info->magic != VSOCK_MAGIC)\ return -EINVAL;\ } while(0) typedef struct _vsock_options { char *key_file; int cid; unsigned int port; unsigned int hash; unsigned int auth; unsigned int flags; } vsock_options; typedef struct _vsock_info { uint64_t magic; void *priv; map_object_t *map; history_info_t *history; char key[MAX_KEY_LEN]; vsock_options args; const fence_callbacks_t *cb; ssize_t key_len; int listen_sock; } vsock_info; struct vsock_hostlist_arg { map_object_t *map; int cid; int fd; }; static int get_peer_cid(int fd, uint32_t *peer_cid) { struct sockaddr_vm svm; socklen_t len; int ret; if (!peer_cid) return -1; len = sizeof(svm); ret = getpeername(fd, (struct sockaddr *) &svm, &len); if (ret < 0) { printf("Error getting peer CID: %s\n", strerror(errno)); return -1; } *peer_cid = svm.svm_cid; return 0; } /* * See if we fenced this node recently (successfully) * If so, ignore the request for a few seconds. * * We purge our history when the entries time out. */ static int check_history(void *a, void *b) { fence_req_t *old = a, *current = b; if (old->request == current->request && old->seqno == current->seqno && !strcasecmp((const char *)old->domain, (const char *)current->domain)) { return 1; } return 0; } static int vsock_hostlist(const char *vm_name, const char *vm_uuid, int state, void *priv) { struct vsock_hostlist_arg *arg = (struct vsock_hostlist_arg *) priv; host_state_t hinfo; struct timeval tv; int ret; uint32_t peer_cid = 0; char peer_cid_str[24]; ret = get_peer_cid(arg->fd, &peer_cid); if (ret < 0) { printf("Unable to get peer CID: %s\n", strerror(errno)); peer_cid_str[0] = '\0'; } else snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid); /* Noops if auth == AUTH_NONE */ if (map_check(arg->map, peer_cid_str, vm_uuid) == 0) { /* if we don't have access to fence this VM, * we should not see it in a hostlist either */ return 0; } strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1); strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1); hinfo.state = state; tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int vsock_hostlist_begin(int fd) { struct timeval tv; char val = (char) RESP_HOSTLIST; tv.tv_sec = 1; tv.tv_usec = 0; return _write_retry(fd, &val, 1, &tv); } static int vsock_hostlist_end(int fd) { host_state_t hinfo; struct timeval tv; int ret; printf("Sending terminator packet\n"); memset(&hinfo, 0, sizeof(hinfo)); tv.tv_sec = 1; tv.tv_usec = 0; ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv); if (ret == sizeof(hinfo)) return 0; return 1; } static int do_fence_request_vsock(int fd, fence_req_t *req, vsock_info *info) { char response = 1; struct vsock_hostlist_arg arg; uint32_t peer_cid = 0; char peer_cid_str[24]; int ret; ret = get_peer_cid(fd, &peer_cid); if (ret < 0) { printf("Unable to get peer CID: %s\n", strerror(errno)); return -1; } snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid); /* Noops if auth == AUTH_NONE */ if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) { printf("CID %u Failed to respond to challenge\n", peer_cid); close(fd); return -1; } ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10); if (ret <= 0) { printf("Remote CID %u failed challenge\n", peer_cid); close(fd); return -1; } dbg_printf(2, "Request %d seqno %d target %s from CID %u\n", req->request, req->seqno, req->domain, peer_cid); switch(req->request) { case FENCE_NULL: response = info->cb->null((char *)req->domain, info->priv); break; case FENCE_ON: if (map_check(info->map, peer_cid_str, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->on((char *)req->domain, peer_cid_str, req->seqno, info->priv); break; case FENCE_OFF: if (map_check(info->map, peer_cid_str, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->off((char *)req->domain, peer_cid_str, req->seqno, info->priv); break; case FENCE_REBOOT: if (map_check(info->map, peer_cid_str, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->reboot((char *)req->domain, peer_cid_str, req->seqno, info->priv); break; case FENCE_STATUS: if (map_check(info->map, peer_cid_str, (const char *)req->domain) == 0) { response = RESP_PERM; break; } response = info->cb->status((char *)req->domain, info->priv); break; case FENCE_DEVSTATUS: response = info->cb->devstatus(info->priv); break; case FENCE_HOSTLIST: arg.map = info->map; arg.fd = fd; vsock_hostlist_begin(arg.fd); response = info->cb->hostlist(vsock_hostlist, &arg, info->priv); vsock_hostlist_end(arg.fd); break; } dbg_printf(3, "Sending response to caller CID %u...\n", peer_cid); if (_write_retry(fd, &response, 1, NULL) < 0) perror("write"); history_record(info->history, req); if (fd != -1) close(fd); return 1; } static int vsock_dispatch(listener_context_t c, struct timeval *timeout) { vsock_info *info; fence_req_t data; fd_set rfds; int n; int client_fd; int ret; struct timeval tv; if (timeout != NULL) memcpy(&tv, timeout, sizeof(tv)); else { tv.tv_sec = 1; tv.tv_usec = 0; } info = (vsock_info *) c; VALIDATE(info); FD_ZERO(&rfds); FD_SET(info->listen_sock, &rfds); n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout); if (n <= 0) { if (errno == EINTR || errno == EAGAIN) n = 0; else dbg_printf(2, "select: %s\n", strerror(errno)); return n; } client_fd = accept(info->listen_sock, NULL, NULL); if (client_fd < 0) { perror("accept"); return -1; } dbg_printf(3, "Accepted vsock client...\n"); ret = _read_retry(client_fd, &data, sizeof(data), &tv); if (ret != sizeof(data)) { dbg_printf(3, "Invalid request (read %d bytes)\n", ret); close(client_fd); return 0; } swab_fence_req_t(&data); if (!verify_request(&data, info->args.hash, info->key, info->key_len)) { printf("Key mismatch; dropping client\n"); close(client_fd); return 0; } dbg_printf(3, "Request %d seqno %d domain %s\n", data.request, data.seqno, data.domain); if (history_check(info->history, &data) == 1) { printf("We just did this request; dropping client\n"); close(client_fd); return 0; } switch(info->args.auth) { case AUTH_NONE: case AUTH_SHA1: case AUTH_SHA256: case AUTH_SHA512: printf("VSOCK request\n"); do_fence_request_vsock(client_fd, &data, info); break; default: printf("XXX Unhandled authentication\n"); } return 0; } static int vsock_config(config_object_t *config, vsock_options *args) { char value[1024]; int errors = 0; #ifdef _MODULE if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0) dset(atoi(value)); #endif if (sc_get(config, "listeners/vsock/@key_file", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for key_file\n", value); args->key_file = strdup(value); } else { args->key_file = strdup(DEFAULT_KEY_FILE); if (!args->key_file) { dbg_printf(1, "Failed to allocate memory\n"); return -1; } } args->hash = DEFAULT_HASH; if (sc_get(config, "listeners/vsock/@hash", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for hash\n", value); if (!strcasecmp(value, "none")) { args->hash = HASH_NONE; } else if (!strcasecmp(value, "sha1")) { args->hash = HASH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->hash = HASH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->hash = HASH_SHA512; } else { dbg_printf(1, "Unsupported hash: %s\n", value); ++errors; } } args->auth = DEFAULT_AUTH; if (sc_get(config, "listeners/vsock/@auth", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for auth\n", value); if (!strcasecmp(value, "none")) { args->hash = AUTH_NONE; } else if (!strcasecmp(value, "sha1")) { args->hash = AUTH_SHA1; } else if (!strcasecmp(value, "sha256")) { args->hash = AUTH_SHA256; } else if (!strcasecmp(value, "sha512")) { args->hash = AUTH_SHA512; } else { dbg_printf(1, "Unsupported auth: %s\n", value); ++errors; } } args->port = DEFAULT_MCAST_PORT; if (sc_get(config, "listeners/vsock/@port", value, sizeof(value)-1) == 0) { dbg_printf(1, "Got %s for port\n", value); args->port = atoi(value); if (args->port <= 0) { dbg_printf(1, "Invalid port: %s\n", value); ++errors; } } return errors; } static int vsock_init(listener_context_t *c, const fence_callbacks_t *cb, config_object_t *config, map_object_t *map, void *priv) { vsock_info *info; int listen_sock, ret; struct sockaddr_vm svm; if (NSS_NoDB_Init(NULL) != SECSuccess) { printf("Could not initialize NSS\n"); return 1; } info = calloc(1, sizeof(*info)); if (!info) return -1; info->priv = priv; info->cb = cb; info->map = map; ret = vsock_config(config, &info->args); if (ret < 0) perror("vsock_config"); else if (ret > 0) printf("%d errors found during vsock listener configuration\n", ret); if (ret != 0) { if (info->args.key_file) free(info->args.key_file); free(info); return -1; } if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) { info->key_len = read_key_file(info->args.key_file, info->key, sizeof(info->key)); if (info->key_len < 0) { printf("Could not read %s; operating without " "authentication\n", info->args.key_file); info->args.auth = AUTH_NONE; info->args.hash = HASH_NONE; info->key_len = 0; } } listen_sock = socket(PF_VSOCK, SOCK_STREAM, 0); if (listen_sock < 0) goto out_fail; memset(&svm, 0, sizeof(svm)); svm.svm_family = AF_VSOCK; svm.svm_cid = VMADDR_CID_ANY; svm.svm_port = info->args.port; if (bind(listen_sock, (struct sockaddr *) &svm, sizeof(svm)) < 0) goto out_fail; if (listen(listen_sock, 1) < 0) goto out_fail; info->magic = VSOCK_MAGIC; info->listen_sock = listen_sock; info->history = history_init(check_history, 10, sizeof(fence_req_t)); *c = (listener_context_t)info; return 0; out_fail: printf("Could not set up listen socket: %s\n", strerror(errno)); if (listen_sock >= 0) close(listen_sock); if (info->args.key_file) free(info->args.key_file); free(info); return -1; } static int vsock_shutdown(listener_context_t c) { vsock_info *info = (vsock_info *)c; VALIDATE(info); info->magic = 0; history_wipe(info->history); free(info->history); free(info->args.key_file); close(info->listen_sock); free(info); return 0; } static listener_plugin_t vsock_plugin = { .name = NAME, .version = VSOCK_VERSION, .init = vsock_init, .dispatch = vsock_dispatch, .cleanup = vsock_shutdown, }; #ifdef _MODULE double LISTENER_VER_SYM(void) { return PLUGIN_VERSION_LISTENER; } const listener_plugin_t * LISTENER_INFO_SYM(void) { return &vsock_plugin; } #else static void __attribute__((constructor)) vsock_register_plugin(void) { plugin_reg_listener(&vsock_plugin); } #endif