Reusable-Cluster-Components-glue--3cff550e1084/.hg_archival.txt0000644000000000000000000000013612120057602024347 0ustar00usergroup00000000000000repo: e3ffdd7ae81c596b2be7e1e110d2c1255161340e node: 3cff550e1084f1accc7782ff371739ec84e31330 Reusable-Cluster-Components-glue--3cff550e1084/.hgignore0000644000000000000000000000221412120057602023063 0ustar00usergroup00000000000000syntax: glob # Autofoo entries *.o *.la *.lo *.loT *.pyc .libs .deps *.cache .cvsignore compile configure configure.status configure.lineno depcomp aclocal.m4 libtool ltmain.sh ltconfig libltdl mkinstalldirs install-sh missing py-compile autom4te* libtool.m4 ltdl.m4 libltdl.tar autoconf autoheader automake include/glue_config.h include/stamp-h1 include/pils/plugin.h include/stamp-h2 ylwrap # BEAM Entries *.beam parser-messages MISC_ERRORS cscope.files cscope.out patches updates logs # OS and Editor Artifacts .DS_Store *.diff *.patch *~ # Project build targets lib/clplumbing/base64_md5_test lib/clplumbing/ipctest lib/clplumbing/ipctransientclient lib/clplumbing/ipctransientserver logd/ha_logd logd/ha_logger logd/logtest lrm/admin/lrmadmin lrm/lrmd/lrmd lrm/test/apitest lrm/test/callbacktest lrm/test/plugintest lrm/test/lrmregtest lrm/test/lrmregtest-heartbeat lrm/test/lrmregtest-lsb lrm/test/regression.sh lrm/test/LRMBasicSanityCheck lrm/test/simple_ops # Misc GPATH GRTAGS GSYMS GTAGS HTML TAGS .gres.* *.orig .gdb_history # Entries better done as regexp's to avoid matching too broadly syntax: regexp ^config\.* README$ Makefile$ Makefile.in$ Reusable-Cluster-Components-glue--3cff550e1084/.hgsigs0000644000000000000000000000064412120057602022552 0ustar00usergroup00000000000000b6dca003bb176978af803eeb33019b6aef3c58b0 0 iEYEABECAAYFAktnGJAACgkQWnQN9wr0w1ywBACghXYwYkv/70Xg5AQMzVjRWKZecIoAnjRUytRoYl+dhhqbhfdXSD+/Bfvw 6007185b487e3f2dc3b24674a9105761b2cde6ea 0 iEYEABECAAYFAktoWfsACgkQWnQN9wr0w1ySZwCfQILyC2VJrCnVEU2zvTIyI7ustDAAn37hhb9JM8JQVKLfPEbqIloz1m3m 979c4ffae287976631a30d10258903aea6fb28fa 0 iEYEABECAAYFAktoY38ACgkQWnQN9wr0w1wHxgCeMZyOt8ccxmIsvIHg4/y6KmqtTVAAn2jn7dOmFMjA8m4ju59YaQ1Bznhb Reusable-Cluster-Components-glue--3cff550e1084/.hgtags0000644000000000000000000000667712120057602022557 0ustar00usergroup0000000000000001ecac8670a6c2e47202a9ce2f5e27e9dcdbeff6 STABLE-1.1.3 19d11d8d62c270c48a3feab5ed66b18897c9cc8d sle11-rc9 2185d55c12e37c48abc239dd1f8b3b9ef012fd6b obs-2.1.2-1 235a71009062702c906cc68f23904ddcbe17535f STABLE-2.0.6 29540582671a9e33ae2122d319c68258346f1a3f STABLE-2.1.1 2cb36a1c01c76ef3e3a449f16b13730c761efff2 STABLE-2.1.3 2ece20ad31a4271076e5c43dd3f2ea25caa55635 Series-Root-1.0 3b8dc33a402daaf7e3754acadd1898c0fe69072f STABLE-2.0.3 45c377d7a35dba92d46321d2f824bc0d9b17f54e obs-2.1.2-24 67a443d135f128ca28f15e4e8999d3e0caabed61 sle11-rc3 68de68ef5f0a7b97a4ff0d9806c598527c8659b8 STABLE-2.0.4 705b21e4b623f7d2fc5c83d99beffb709905c996 STABLE-0.4.9c 7190f69e29a08350bcec753509eb37f53593334a beta-2.99.0 7f90244e5c25372e70178f77f44c76a8564e1665 SLE10-SP1 7f90244e5c25372e70178f77f44c76a8564e1665 STABLE-2.1.0 823208439a98179d7c01d6eed1db50dd96802663 sle11-rc7 86fa06f08a123868eb272a20bf850cec1805a12f STABLE-2.0.8 97d025dd33648a1e50a3a1bf40573669440dd1b6 obs-2.1.2-4 9b34f480b8e8966e9ed4276507cc562564763720 beta-2.99.2 9eb2a4db4ff595d18302426029b03153fad77ef7 beta-2.99.1 a230062a445096b89cf75bef85e285ee55626a78 obs-2.1.2-2 af867b71bcc645f3d3c56fe8fdd883b17a851e46 STABLE-2.0.0 b4a0a0ffd15eb2dd1285bdbd86ea9716a9d0bf36 STABLE-2.0.5 b906db882c37647abfd21fa1473950445ad7813c STABLE-2.1.2 ba476a3948ea0cf52098fa050a27a8856a214825 sle11-rc2 be0d49da51a810e870356b7f2a52013e5c775c0d Beta-0.4.9a c77ad4549888539e7fc9a6b56cccdb1403749198 STABLE-0.4.9e c7d672b9f3ece79ad26fb8a7df20265bcb596515 sle11-beta6 cf0265eed1b5b3b3f25f7e56eb807d21ca261d68 SLES10-GA-2.0.7-1.5 cf0265eed1b5b3b3f25f7e56eb807d21ca261d68 STABLE-2.0.7 d1899e1eecc09b7a6e66a02609408272bb856c6a STABLE-1.1.5 dae6b0b3e109afc5df29a7127ab6dd9e1bd0a20a sle11-rc5 e3691501a2d0631c3796b6a728fadf7d90691203 obs-2.1.2-15 e3855af19554339204b5b2b2a199a7bc31e22843 STABLE-2.0.1 e3855af19554339204b5b2b2a199a7bc31e22843 STABLE-2.0.2 e6637f62c87ada212a83942ec5b2a4bf30b98c3f Series-Root-1.2 f6c2cd2593f365f984ce051db61466738ac05dcd Beta-0.4.9f 940fa13f6a0a929d15a01af9a0b62c16e4d2706a glue-1.0 130b1d7af88912d077d32a7c386c3c94d0b2da16 glue-1.0.2-rc1 78894a112c0a134dc709d2a8772085180444c40c glue-1.0.2-rc2 7700902a4de3ee84fa2007a4b4602693c5ac26a8 glue-1.0.2-rc2a 97fcdf789e174b0a0b23e28dcabe2f7d579d426f glue-1.0.2 0a64e6f77894da1364b17dc3c73b65561717f4aa glue-1.0.3 0a64e6f77894da1364b17dc3c73b65561717f4aa glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 979c4ffae287976631a30d10258903aea6fb28fa glue-1.0.3 979c4ffae287976631a30d10258903aea6fb28fa glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 9bcd134f1ebff7baf80f4b21c3b5f620b0ee976e glue-1.0.3 9bcd134f1ebff7baf80f4b21c3b5f620b0ee976e glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 0000000000000000000000000000000000000000 glue-1.0.3 2e33ecd820b2673755d1280a259489a026921f63 glue-1.0.3 761edff8c35ea2cdf3e1bd37d600b06233e61d4f glue-1.0.4-rc1 3229873980e1028bf05de81f5bafccb3a92b9aa4 glue-1.0.4 3af80b93d9e5d5e441f3f4c3aad16775ea27d2d9 glue-1.0.5 1c87a0c58c59fc384b93ec11476cefdbb6ddc1e1 glue-1.0.6 61200fbe18358e420cdc2037d87e803e150c1eac glue-1.0.7-rc1 5e06b2ddd24b37ad6c1c25d958d7a9dda7d02f93 glue-1.0.7 5740338816e1ff07d0e37f36214f442e183984d7 glue-1.0.8-rc1 c69dc6ace936f501776df92dab3d611c2405f69e glue-1.0.8 0a08a469fdc8a0db1875369497bc83c0523ceb21 glue-1.0.9 12055ca2b025ab250a544701edaa1f5aaf63aef1 glue-1.0.10 02bdcf58f9a098b717784746308e199e12eeb005 glue-1.0.11 Reusable-Cluster-Components-glue--3cff550e1084/AUTHORS0000644000000000000000000000121512120057602022330 0ustar00usergroup00000000000000Alan Robertson Andreas Mock Andrew Beekhof Dave Blaschke David Lee Dejan Muhamedagic Hannes Eder Huang Zhen Junko Ikeda Lars Marowsky-Bree Martin Bene Phil Carns Satomi Taniguchi Sean Reifschneider Sebastian Reitenbach Serge Dubrouski Simon Horman Xinwei Hu Reusable-Cluster-Components-glue--3cff550e1084/COPYING0000644000000000000000000004310312120057602022315 0ustar00usergroup00000000000000 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. Reusable-Cluster-Components-glue--3cff550e1084/COPYING.LIB0000644000000000000000000006350212120057602022727 0ustar00usergroup00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! Reusable-Cluster-Components-glue--3cff550e1084/ChangeLog0000644000000000000000000002754012120057602023043 0ustar00usergroup00000000000000* Mon Oct 15 2012 Dejan Muhamedagic , Lars Ellenberg , and many others - stable release 1.0.11 - lrmd: set max-children depending on the number of processors - lrmd: don't send parameters from ops back to crmd - stonith: external/libvirt: support for reboot reset method - hb_report: node's type got optional - hb_report: make use of bash trace features - hb_report: compatibility code for pacemaker v1.1.8 - build: link libstonith with stonith2 agents * Mon Jul 16 2012 Dejan Muhamedagic , Lars Ellenberg , and many others - stable release 1.0.10 - clplumbing: ipc: fix message size checks (bnc#752231) - clplumbing: load bz2 compression module by default - clplumbing: cl_msg: try compressing message before rejecting it as too big - clplumbing: cl_msg: don't use traditional compression by default - clplumbing: cl_msg: increase compression threshold - clplumbing: fix memleak for Gmain_timeout - LRM: lrmd: add basic authentication (lf#2547) - LRM: lrmd: use the resource timeout as an override to the default dbus timeout for upstart RA - LRM: lrmd: if set, get max-children from the LRMD_MAX_CHILDREN environment var - stonith: add CRM stonith resource name to log messages (bnc#728579) - stonith: adjust timeouts in the meta-data template (bnc#733337) - stonith: external/vcenter: return list of configured hosts on gethosts - stonith: external/libvirt: add more search strings for domain start and stop - stonith: rhcs: pass the action via stdin too - stonith: rhcs: avoid false error if parameter isn't set - logd: remove runlevel 4 from the LSB info section in the logd init script (bnc#744120) - logd: add try-restart action to the logd init script - sbd: Use async IO for disk reads to increase resilience against hung IO (bnc#738295) - sbd: Handle IO errors during slot allocation properly (bnc#753559) - sbd: Debug mode added (bnc#753559, bnc#738295) - hb_report: improve performance - hb_report: get corosync blackbox records if available - hb_report: add node time information * Mon Nov 28 2011 Dejan Muhamedagic , Lars Ellenberg , and many others - stable release 1.0.9 - stonith: external/ipmi: add missing double quote - stonith: external/ipmi: add the priv parameter (ipmitool -L) - LRM: lrmd: set op status to cancelled for running monitor operations - ha_log: increase MAXENTITY size to accommodate long stonith strings - hb_report: improve destination directory handling (bnc#727295) * Tue Oct 18 2011 Dejan Muhamedagic , Lars Ellenberg , and many others - stable release 1.0.8 - cl_log: log spamming control - LRM: raexecocf: list resource agents properly (bnc#664409) - LRM: lrmd: allow storing parameters in local files (lf#2415) - LRM: lrmd: limit number of "stayed too long in operation list" log messages (bnc#636576) - stonith: external/libvirt: new plugin for libvirt virtualization technologies - stonith: external/vcenter: new plugin - stonith: external/hetzner: new plugin - stonith: sbd: support for multiple devices - stonith: sbd: Fix timeout setting on archs where int != long (bnc#635690) - stonith: sbd: abort start if watchdog cannot be initialized (bnc#680109) - stonith: sbd: Make failing to set the watchdog timeout non-fatal but annoying - stonith: sbd: Make the restart interval for servants configurable - stonith: sbd: Maximize scheduler and IO priority in the child processes (bnc#702907) - stonith: external/sbd: Fix ordering of arguments in reset - stonith: external/ipmi: fix unique parameters' attributes - stonith: external/rackpdu: split off assignment from local to make it work with non-bash shells - stonith: external: avoid false error if parameter isn't set (bnc#646205) - hb_report: add .info files with the last byte pos for all logs - hb_report: use sudo for remove collectors if connecting with user other than root - hb_report: install debuginfo packages on platforms with zypper (bnc#641979) - hb_report: improve detecting ssh user * Tue Nov 30 2010 Dejan Muhamedagic , Lars Ellenberg , and many others - stable release 1.0.7 - clplumbing: ipc: adjust socket buffers size when adjusting ipc queue length - logd: add a SIGHUP signal handler to timely close/open log files - logd: use buffered io with fflush and fsync - logd: reopen logfiles on inode change (logrotate) - clplumbing: cl_log: keep logfiles open, but default to non-buffered io (lf#2470) - clplumbing: cl_log: add new optional common syslog message prefix - stonith: use ST_DEVICEID for the short description in meta-data - stonith: external: interpret properly exit codes from external stonith plugins (bnc#630357) - stonith: external: avoid false out of memory error if a parameter isn't set (bnc#646205) - stonith: external: check if PATH already contains GLUE_SHARED_DIR (memory leak, lf#2484) - stonith(8): reduce the number of stonith plugin invocations (bnc#630357) - stonith(8): use cl_log for logging if invoked by stonithd (pcmk 1.1) - stonith: external/sbd: make sbd use realtime priority for IO (works only with CFQ) - stonith: cyclades: add the serial_port parameter to the meta-data - stonith: external/riloe: add support for http proxies - stonith: external/ipmi: provide opt param "passwd_method" to hide the ipmi password from config and logs - stonith: external/nut: support for the Network UPS Tools - stonith: external/rackpdu: remove displaced local command - stonith: rcd_serial: rename dtr|rts parameter to dtr_rts - configure: test for POSIX signals (fixes rcd_serial) * Fri Jul 9 2010 Dejan Muhamedagic - stable release 1.0.6 - clplumbing: Add identity info of the user on the other side of socket - ha_logger: log strings longer than 1024 - lrmd: remove operation history on client unregister (lf#2161) - lrmd: don't allow cancelled operations to get back to the repeating op list (lf#2417) - lrmd: exclude stonith resources from child count (bnc#612387) - lrmd,clientlib: asynchronous resource delete notification (lf#2439) - stonith: add -V (version) to stonith - stonith: add -E option to get the configuration from the environment - stonith: ha_log: feed the message to stdout and not on command line - stonith: external/sbd,xen0: fix wrong reference from ha_log to ha_log.sh (deb#585120) - stonith: external/sbd: reduce monitoring - stonith: external/rackpdu: check the snmpset and snmpwalk exit codes - hb_report: create cib.txt after sanitizing the CIB (lf#2415) * Mon Apr 15 2010 Dejan Muhamedagic - stable release 1.0.5 - clplumbing: revert changeset 81ad41d14f72 which breaks the ABI * Mon Apr 12 2010 Dejan Muhamedagic - stable release 1.0.4 - clplumbing: fix memory leak in cl_msg/lrmd (lf#1841,2389) - clplumbing: Add identity info of the user on the other side of socket - clplumbing: Fix erroneous "Stack hogger failed 0xffffffff" warnings - lrmd: fix possible null pointer dereference - lrmd: raise severity from debug to info for some log messages - lrmd: on shutdown exit once all operations finished (lf#2340) - lrmd: don't add the cancel option in flush to the running operations (bnc#578644) - lrmd: check if tables exist before free_str_table and prevent segfault (bnc#587887) - stonith: new external/ippower9258 plugin - stonith: external/sbd: fix status operation - stonith: external/sbd: add support for heartbeat - stonith: external/ibmrsa-telnet: fix ha_log.sh invocation - stonith: external/ibmrsa-telnet: fix expect regex - stonith: external/ipmi: make reset work when the node is off - stonith: external/riloe: log error message on unrecognized power method - hb_report: don't create dot files if there are more than 20 PE files - hb_report: make dot and png files for PE inputs (if there are not too many) - hb_report: do not filter CIB/PE files by default (use -s to force filtering) - hb_report: add -Z option to force destination directory cleanup - hb_report: allow for default destination - hb_report: when creating cts reports get information from the log - hb_report: new option -d to keep the directory - hb_report: don't give up early when creating backtraces (lf#2350) * Tue Feb 02 2010 Dejan Muhamedagic - bugfix release 1.0.3 - lrmd: don't flush operations which don't belong to the requesting client (lf#2161) * Mon Feb 01 2010 Dejan Muhamedagic and MANY others - stable release 1.0.2 - clplumbing: fix a potential resource leak in cl_random (bnc#525393) - clplumbing: change the default log format to syslog format - lrmd: log outcome of monitor once an hour - lrmd: lookup clients by name (lf#2161) - lrmd: remove operation history on client unregister (lf#2161) - lrmd: fix return code on LSB class RA exec failure (lf#2194) - lrmd: close the logd fd too when executing agents (lf#2267) - lrmd: restore reset scheduler for children (bnc#551971,lf#2296) - lrmd: reset scheduler and priority for children (resource operations) - lrmadmin: fix -E option - lrmadmin moved to the sbindir - stonith: support for RHCS fence agents - stonith: external/dracmc-telnet: stonith plugin for Dell Drac/MC Blade Enclosure and Cyclades terminal server - stonith: sbd plugin - stonith: apcmastersnmp plugin (bnc#518689) - stonith: bladehpi plugin (bnc#510299) - stonith: WTS MPC: new SNMP based plugin - stonith: meatclient: add -w option to wait until we can connect - stonith: add -m option to stonith(8) to display metadata (lf#2279) - stonith: external: log using ha_log.sh (lf#2294,1971) - stonith: external: log output of plugins (bnc#548699,bnc#553340) - stonith: external: log messages immediately on manage and status calls - stonith: external: remove dependency on .ocf-shellfuncs (lf#2249) - stonith: external/riloe: make sure that host is turned on after power off/on reset (lf#2282) - stonith: external/riloe: fix check for ilo_can_reset - stonith: external/riloe: workaround for the iLO double close in RIBCL (bnc#553340) - stonith: external/ipmi: add explanation on reset and power off (LF 2071) - stonith: external/ibmrsa-telnet: add support for later RSA cards - stonith: cyclades: fix for support for newer PM10 firmware (lf#1938) - stonith: wti_nps: add support for internet power switch model (bnc#539912) - stonith: wti_mpc: support for MIB versions 1 and 3 - stonith: external/sbd: fix definition of sector_size for s390x (bnc#542827) - stonith: external/sbd: make nodename comparison case insensitive (bnc#534445) - stonith: external/sbd: describe "dump" command in help (bnc#529575) - stonith: external/sbd: Accept -h (bnc#529574) - stonith: external/xen0: add run_dump parameter to dump core before resetting a node - hb_report: add man page hb_report.8 - hb_report: add -V (version) option - hb_report: add support for corosync - hb_report: add -v option (debugging) - hb_report: options -C and -D are obsoleted - hb_report: combine log/events if there is no loghost - hb_report: extract important events from the logs - logd: add init script - rpm spec: start logd by default - doc: new README for wti_mpc - doc: move stonith README files to the doc directory - doc: convert man pages to xml - build: /usr/share/heartbeat replaced by /usr/share/cluster-glue - build: enable IPMI and hpi support - build: include time.h in ipcsocket.c and proctrack.c (lf#2263) - build: output documentation directory from configure (lf#2276) * Thu Oct 23 2008 Lars Marowsky-Bree and MANY others - beta release 2.99.2 - stonith: external/kdumpcheck: new plugin - stonith: external/drac5: new plugin - stonith: drac3: initialize curl properly and workaround xml parsing problem (lf#1730) - stonith external/riloe: a new implementation for HP iLO devices * Tue Sep 23 2008 Lars Marowsky-Bree and MANY others - beta release 2.99.1 - stonith: bladehpi: fix a mix of a threaded library and not threaded stonithd (bnc#389344) - stonith: external/riloe: fix check for ilo_can_reset * Tue Aug 19 2008 Andrew Beekhof and MANY others - beta release 2.99.0 Reusable-Cluster-Components-glue--3cff550e1084/GNUmakefile0000644000000000000000000000272512120057602023341 0ustar00usergroup00000000000000# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -include Makefile PACKAGE ?= cluster-glue TARFILE = $(PACKAGE).tar.bz2 RPM_ROOT = $(shell pwd) RPM_OPTS = --define "_sourcedir $(RPM_ROOT)" \ --define "_specdir $(RPM_ROOT)" \ --define "_srcrpmdir $(RPM_ROOT)" getdistro = $(shell test -e /etc/SuSE-release || echo fedora; test -e /etc/SuSE-release && echo suse) DISTRO ?= $(call getdistro) TAG ?= tip hgarchive: rm -f $(TARFILE) hg archive -t tbz2 -r $(TAG) $(TARFILE) echo `date`: Rebuilt $(TARFILE) srpm: hgarchive rm -f *.src.rpm @echo To create custom builds, edit the flags and options in $(PACKAGE)-$(DISTRO).spec first rpmbuild -bs --define "dist .$(DISTRO)" $(RPM_OPTS) $(PACKAGE)-$(DISTRO).spec rpm: srpm rpmbuild $(RPM_OPTS) --rebuild $(RPM_ROOT)/*.src.rpm Reusable-Cluster-Components-glue--3cff550e1084/Makefile.am0000644000000000000000000000261012120057602023314 0ustar00usergroup00000000000000# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure DRF/config-h.in \ DRF/stamp-h.in libtool.m4 ltdl.m4 libltdl.tar SUBDIRS = include $(LIBLTDL_DIR) replace lib lrm logd \ hb_report doc config install-exec-local: $(INSTALL) -d $(DESTDIR)/$(HA_COREDIR) -$(INSTALL) -d -m 700 -o root $(DESTDIR)/$(HA_COREDIR)/root -$(INSTALL) -d -m 700 -o nobody $(DESTDIR)/$(HA_COREDIR)/nobody $(INSTALL) -d -m 700 $(DESTDIR)/$(HA_COREDIR)/$(GLUE_DAEMON_USER) -chown $(GLUE_DAEMON_USER) $(DESTDIR)/$(HA_COREDIR)/$(GLUE_DAEMON_USER) # Use chown because $(GLUE_DAEMON_USER) may not exist yet dist-clean-local: rm -f autoconf automake autoheader $(TARFILE) .PHONY: Reusable-Cluster-Components-glue--3cff550e1084/NEWS0000644000000000000000000000000212120057602021750 0ustar00usergroup00000000000000 Reusable-Cluster-Components-glue--3cff550e1084/README0000644000000000000000000000000212120057602022131 0ustar00usergroup00000000000000 Reusable-Cluster-Components-glue--3cff550e1084/autogen.sh0000755000000000000000000001176112120057602023270 0ustar00usergroup00000000000000#!/bin/sh # # License: GNU General Public License (GPL) # Copyright 2001 horms # (heavily mangled by alanr) # # bootstrap: set up the project and get it ready to make # # Basically, we run autoconf, automake and libtool in the # right way to get things set up for this environment. # # We also look and see if those tools are installed, and # tell you where to get them if they're not. # # Our goal is to not require dragging along anything # more than we need. If this doesn't work on your system, # (i.e., your /bin/sh is broken) send us a patch. # # This code loosely based on the corresponding named script in # enlightenment, and also on the sort-of-standard autoconf # bootstrap script. # Run this to generate all the initial makefiles, etc. testProgram() { cmd=$1 if [ -z "$cmd" ]; then return 1; fi arch=`uname -s` # Make sure the which is in an if-block... on some platforms it throws exceptions # # The ERR trap is not executed if the failed command is part # of an until or while loop, part of an if statement, part of a && # or || list. if which $cmd /dev/null 2>&1 then : else return 1 fi # The GNU standard is --version if $cmd --version /dev/null 2>&1 then return 0 fi # Maybe it suppports -V instead if $cmd -V /dev/null 2>&1 then return 0 fi # Nope, the program seems broken return 1 } gnu="ftp://ftp.gnu.org/pub/gnu" for command in autoconf213 autoconf253 autoconf259 autoconf do if testProgram $command == 1 then autoconf=$command autoheader=`echo "$autoconf" | sed -e 's/autoconf/autoheader/'` autom4te=`echo "$autoconf" | sed -e 's/autoconf/autmo4te/'` autoreconf=`echo "$autoconf" | sed -e 's/autoconf/autoreconf/'` autoscan=`echo "$autoconf" | sed -e 's/autoconf/autoscan/'` autoupdate=`echo "$autoconf" | sed -e 's/autoconf/autoupdate/'` ifnames=`echo "$autoconf" | sed -e 's/autoconf/ifnames/'` fi done for command in automake14 automake-1.4 automake15 automake-1.5 automake17 automake-1.7 automake19 automake-1.9 automake-1.11 automake do if testProgram $command then : OK $pkg is installed automake=$command aclocal=`echo "$automake" | sed -e 's/automake/aclocal/'` fi done for command in libtool14 libtool15 libtool glibtool do URL=$gnu/$pkg/ if testProgram $command then libtool=$command libtoolize=`echo "$libtool" | sed -e 's/libtool/libtoolize/'` fi done if [ -z $autoconf ]; then echo You must have autoconf installed to compile the cluster-glue package. echo Download the appropriate package for your system, echo or get the source tarball at: $gnu/autoconf/ exit 1 elif [ -z $automake ]; then echo You must have automake installed to compile the cluster-glue package. echo Download the appropriate package for your system, echo or get the source tarball at: $gnu/automake/ exit 1 elif [ -z $libtool ]; then echo You must have libtool installed to compile the cluster-glue package. echo Download the appropriate package for your system, echo or get the source tarball at: $gnu/libtool/ exit 1 fi oneline() { read x; echo "$x" } LT_version=`$libtool --version | oneline | sed -e 's%^[^0-9]*%%' -e s'% .*%%'` LT_majvers=`echo "$LT_version" | sed -e 's%\..*%%'` LT_minvers=`echo "$LT_version" | sed -e 's%^[^.]*\.%%' ` LT_minnum=`echo "$LT_minvers" | sed -e 's%[^0-9].*%%'` if [ $LT_majvers -lt 1 ] || [ $LT_majvers = 1 -a $LT_minnum -lt 4 ] then echo "Minimum version of libtool is 1.4. You have $LT_version installed." exit 1 fi # Create local copies so that the incremental updates will work. rm -f ./autoconf ./automake ./autoheader ./libtool ln -s `which $libtool` ./libtool ln -s `which $autoconf` ./autoconf ln -s `which $automake` ./automake ln -s `which $autoheader` ./autoheader printf "$autoconf:\t" $autoconf --version | head -n 1 printf "$automake:\t" $automake --version | head -n 1 rm -rf libltdl libltdl.tar echo $libtoolize --ltdl --force --copy # Unset GREP_OPTIONS as any coloring can mess up the AC_CONFIG_AUX_DIR matching patterns GREP_OPTIONS= $libtoolize --ltdl --force --copy arch=`uname -s` # Disable the errors on FreeBSD until a fix can be found. if [ ! "$arch" = "FreeBSD" ]; then set -e # # All errors are fatal from here on out... # The shell will complain and exit on any "uncaught" error code. # # # And the trap will ensure sure some kind of error message comes out. # trap 'echo ""; echo "$0 exiting due to error (sorry!)." >&2' 0 fi # Emulate the old --ltdl-tar option... # If the libltdl directory is required we will unpack it later tar -cf libltdl.tar libltdl rm -rf libltdl echo $aclocal $ACLOCAL_FLAGS $aclocal $ACLOCAL_FLAGS echo $autoheader $autoheader echo $automake --add-missing --include-deps --copy $automake --add-missing --include-deps --copy echo $autoconf $autoconf test -f libtool.m4 || touch libtool.m4 test -f ltdl.m4 || touch ltdl.m4 echo Now run ./configure trap '' 0 Reusable-Cluster-Components-glue--3cff550e1084/cluster-glue-fedora.spec0000644000000000000000000001445312120057602026015 0ustar00usergroup00000000000000# Keep around for when/if required ## define alphatag XXX %define gname haclient %define uname hacluster %define nogroup nobody # Directory where we install documentation %global glue_docdir %{_defaultdocdir}/%{name}-%{version} # When downloading directly from Mercurial, it will automatically add this prefix # Invoking 'hg archive' wont but you can add one with: hg archive -t tgz -p "Reusable-Cluster-Components-" -r $upstreamversion $upstreamversion.tar.gz %global upstreamprefix Reusable-Cluster-Components- %global upstreamversion d97b9dea436e Name: cluster-glue Summary: Reusable cluster components Version: 1.0.11 Release: 1%{?dist} License: GPLv2+ and LGPLv2+ Url: http://www.linux-ha.org/wiki/Cluster_Glue Group: System Environment/Base Source0: cluster-glue.tar.bz2 Requires: perl-TimeDate Requires: cluster-glue-libs = %{version}-%{release} # Directives to allow upgrade from combined heartbeat packages in Fedora11 Provides: heartbeat-stonith = 3.0.0-1 Provides: heartbeat-pils = 3.0.0-1 Obsoletes: heartbeat-stonith < 3.0.0-1 Obsoletes: heartbeat-pils < 3.0.0-1 Obsoletes: heartbeat-common ## Setup/build bits BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) # Build dependencies BuildRequires: automake autoconf libtool pkgconfig which BuildRequires: bzip2-devel glib2-devel python-devel libxml2-devel libaio-devel BuildRequires: OpenIPMI-devel openssl-devel BuildRequires: libxslt docbook-dtds docbook-style-xsl BuildRequires: help2man BuildRequires: asciidoc %if 0%{?fedora} BuildRequires: libcurl-devel libnet-devel %endif %if 0%{?fedora} || 0%{?centos} > 4 || 0%{?rhel} > 4 BuildRequires: libtool-ltdl-devel openhpi-devel BuildRequires: net-snmp-devel >= 5.4 %else BuildRequires: gcc-c++ %endif %if 0%{?fedora} > 11 || 0%{?centos} > 5 || 0%{?rhel} > 5 BuildRequires: libuuid-devel %else BuildRequires: e2fsprogs-devel %endif %prep %setup -q -n cluster-glue ./autogen.sh # RHEL <= 5 does not support ./configure --docdir=, # hence, use this ugly hack %if 0%{?centos} <= 5 || 0%{?rhel} <= 5 export docdir=%{glue_docdir} %configure \ --enable-fatal-warnings=yes \ --with-daemon-group=%{gname} \ --with-daemon-user=%{uname} \ --localstatedir=%{_var} \ --libdir=%{_libdir} %else %configure \ --enable-fatal-warnings=yes \ --with-daemon-group=%{gname} \ --with-daemon-user=%{uname} \ --localstatedir=%{_var} \ --libdir=%{_libdir} \ --docdir=%{glue_docdir} %endif %build make %{?jobs:-j%jobs} docdir=%{glue_docdir} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} docdir=%{glue_docdir} ## tree fix up # Dont package static libs find %{buildroot} -name '*.a' -exec rm {} \; find %{buildroot} -name '*.la' -exec rm {} \; %clean rm -rf %{buildroot} # cluster-glue %description A collection of common tools that are useful for writing cluster managers such as Pacemaker. Provides a local resource manager that understands the OCF and LSB standards, and an interface to common STONITH devices. %files %defattr(-,root,root) %dir %{_datadir}/%{name} %{_sysconfdir}/init.d/logd %{_datadir}/%{name}/ha_cf_support.sh %{_datadir}/%{name}/openais_conf_support.sh %{_datadir}/%{name}/utillib.sh %{_datadir}/%{name}/combine-logs.pl %{_datadir}/%{name}/ha_log.sh %{_sbindir}/ha_logger %{_sbindir}/hb_report %{_sbindir}/lrmadmin %{_sbindir}/cibsecret %{_sbindir}/meatclient %{_sbindir}/stonith %dir %{_libdir}/heartbeat %dir %{_libdir}/heartbeat/plugins %dir %{_libdir}/heartbeat/plugins/RAExec %dir %{_libdir}/heartbeat/plugins/InterfaceMgr %dir %{_libdir}/heartbeat/plugins/compress %{_libdir}/heartbeat/lrmd %{_libdir}/heartbeat/ha_logd %{_libdir}/heartbeat/plugins/RAExec/*.so %{_libdir}/heartbeat/plugins/InterfaceMgr/*.so %{_libdir}/heartbeat/plugins/compress/*.so %dir %{_libdir}/stonith %dir %{_libdir}/stonith/plugins %dir %{_libdir}/stonith/plugins/stonith2 %{_libdir}/stonith/plugins/external %{_libdir}/stonith/plugins/stonith2/*.so %{_libdir}/stonith/plugins/stonith2/*.py* %exclude %{_libdir}/stonith/plugins/external/ssh %exclude %{_libdir}/stonith/plugins/stonith2/null.so %exclude %{_libdir}/stonith/plugins/stonith2/ssh.so %{_libdir}/stonith/plugins/xen0-ha-dom0-stonith-helper %dir %{_var}/lib/heartbeat %dir %{_var}/lib/heartbeat/cores %dir %attr (0700, root, root) %{_var}/lib/heartbeat/cores/root %dir %attr (0700, nobody, %{nogroup}) %{_var}/lib/heartbeat/cores/nobody %dir %attr (0700, %{uname}, %{gname}) %{_var}/lib/heartbeat/cores/%{uname} %{_mandir}/man1/* %{_mandir}/man8/* %doc doc/stonith/README* %doc logd/logd.cf %doc AUTHORS %doc COPYING %doc ChangeLog # cluster-glue-libs %package -n cluster-glue-libs Summary: Reusable cluster libraries Group: Development/Libraries Obsoletes: libheartbeat2 %description -n cluster-glue-libs A collection of libraries that are useful for writing cluster managers such as Pacemaker. %pre getent group %{gname} >/dev/null || groupadd -r %{gname} getent passwd %{uname} >/dev/null || \ useradd -r -g %{gname} -d %{_var}/lib/heartbeat/cores/hacluster -s /sbin/nologin \ -c "cluster user" %{uname} exit 0 %post -n cluster-glue-libs -p /sbin/ldconfig %postun -n cluster-glue-libs -p /sbin/ldconfig %files -n cluster-glue-libs %defattr(-,root,root) %{_libdir}/lib*.so.* %doc AUTHORS %doc COPYING.LIB # cluster-glue-libs-devel %package -n cluster-glue-libs-devel Summary: Headers and libraries for writing cluster managers Group: Development/Libraries Requires: cluster-glue-libs = %{version}-%{release} Obsoletes: libheartbeat-devel %description -n cluster-glue-libs-devel Headers and shared libraries for a useful for writing cluster managers such as Pacemaker. %files -n cluster-glue-libs-devel %defattr(-,root,root) %dir %{_libdir}/heartbeat/plugins %dir %{_libdir}/heartbeat/plugins/test %dir %{_libdir}/heartbeat %dir %{_datadir}/%{name} %{_libdir}/lib*.so %{_libdir}/heartbeat/ipctest %{_libdir}/heartbeat/ipctransientclient %{_libdir}/heartbeat/ipctransientserver %{_libdir}/heartbeat/transient-test.sh %{_libdir}/heartbeat/base64_md5_test %{_libdir}/heartbeat/logtest %{_includedir}/clplumbing %{_includedir}/heartbeat %{_includedir}/stonith %{_includedir}/pils %{_datadir}/%{name}/lrmtest %{_libdir}/heartbeat/plugins/test/test.so %{_libdir}/stonith/plugins/external/ssh %{_libdir}/stonith/plugins/stonith2/null.so %{_libdir}/stonith/plugins/stonith2/ssh.so %doc AUTHORS %doc COPYING %doc COPYING.LIB %changelog Reusable-Cluster-Components-glue--3cff550e1084/cluster-glue-suse.spec0000644000000000000000000001743312120057602025535 0ustar00usergroup00000000000000# # Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # # norootforbuild # # Since this spec file supports multiple distributions, ensure we # use the correct group for each. # %define uid 90 %define gname haclient %define uname hacluster # Directory where we install documentation %global glue_docdir %{_defaultdocdir}/%{name} Name: cluster-glue Summary: Reusable cluster components Version: 1.0.11 Release: 1%{?dist} License: GPL v2 or later; LGPL v2.1 or later Url: http://www.linux-ha.org/wiki/Cluster_Glue Group: Productivity/Clustering/HA Source: cluster-glue.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-build AutoReqProv: on BuildRequires: automake autoconf libtool e2fsprogs-devel glib2-devel pkgconfig python-devel libxml2-devel BuildRequires: libnet net-snmp-devel OpenIPMI-devel openhpi-devel BuildRequires: libxslt docbook_4 docbook-xsl-stylesheets BuildRequires: help2man BuildRequires: asciidoc Obsoletes: heartbeat-common Provides: heartbeat-common Requires(pre): /usr/sbin/groupadd /usr/bin/getent /usr/sbin/useradd # SLES10 needs tcpd-devel but doesn't have libcurl # in SLES10 docbook has no dependency on sgml-skel %if 0%{?suse_version} < 1020 BuildRequires: tcpd-devel BuildRequires: sgml-skel %else BuildRequires: libcurl-devel %endif %description A collection of common tools derived from the Heartbeat project that are useful for writing cluster managers such as Pacemaker. Provides a local resource manager that understands the OCF and LSB standards, and an interface to common STONITH devices. %package -n libglue2 License: GPL v2 only; GPL v2 or later; LGPL v2.1 or later Summary: The Pacemaker scalable High-Availability cluster resource manager Group: Productivity/Clustering/HA Obsoletes: libheartbeat2 Provides: libheartbeat2 Requires: %{name} = %{version}-%{release} %description -n libglue2 A collection of libraries that are useful for writing cluster managers such as Pacemaker. %package -n libglue-devel License: GPL v2 only; GPL v2 or later; LGPL v2.1 or later Summary: The Pacemaker scalable High-Availability cluster resource manager Group: Development/Libraries/C and C++ Requires: %{name} = %{version}-%{release} Requires: libglue2 = %{version}-%{release} Obsoletes: libheartbeat-devel Provides: libheartbeat-devel %description -n libglue-devel Headers and shared libraries for a useful for writing cluster managers such as Pacemaker. %prep ########################################################### %setup -n cluster-glue -q ########################################################### %build CFLAGS="${CFLAGS} ${RPM_OPT_FLAGS}" export CFLAGS ./autogen.sh # SLES <= 10 does not support ./configure --docdir=, # hence, use this ugly hack %if 0%{?suse_version} < 1020 export docdir=%{glue_docdir} %configure \ --enable-fatal-warnings=yes \ --with-package-name=%{name} \ --with-daemon-group=%{gname} \ --with-daemon-user=%{uname} %else %configure \ --enable-fatal-warnings=yes \ --with-package-name=%{name} \ --with-daemon-group=%{gname} \ --with-daemon-user=%{uname} \ --docdir=%{glue_docdir} %endif make %{?_smp_mflags} docdir=%{glue_docdir} ########################################################### %install ########################################################### make DESTDIR=$RPM_BUILD_ROOT docdir=%{glue_docdir} install # Dont package static libs or compiled python find $RPM_BUILD_ROOT -name '*.a' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.la' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.pyc' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.pyo' -type f -print0 | xargs -0 rm -f test -d $RPM_BUILD_ROOT/sbin || mkdir $RPM_BUILD_ROOT/sbin ( cd $RPM_BUILD_ROOT/sbin ln -s /etc/init.d/logd rclogd ) ########################################################### %clean ########################################################### if [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ] then rm -rf $RPM_BUILD_ROOT fi rm -rf $RPM_BUILD_DIR/cluster-glue ########################################################### %pre if getent group %{gname} >/dev/null then : OK group haclient already present else /usr/sbin/groupadd -o -r -g %{uid} %{gname} 2>/dev/null || : fi if getent passwd %{uname} >/dev/null then : OK hacluster user already present else /usr/sbin/useradd -r -g %{gname} -c "heartbeat processes" \ -d %{_var}/lib/heartbeat/cores/%{uname} -o -u %{uid} \ %{uname} 2>/dev/null || : fi %preun %stop_on_removal logd %post %{insserv_force_if_yast logd} %postun %insserv_cleanup %post -n libglue2 /sbin/ldconfig %postun -n libglue2 /sbin/ldconfig %files ########################################################### %defattr(-,root,root) %dir %{_libdir}/heartbeat %dir %{_var}/lib/heartbeat %dir %{_var}/lib/heartbeat/cores %dir %attr (0700, root, root) %{_var}/lib/heartbeat/cores/root %dir %attr (0700, nobody, nobody) %{_var}/lib/heartbeat/cores/nobody %dir %attr (0700, %{uname}, %{gname}) %{_var}/lib/heartbeat/cores/%{uname} %dir %{_libdir}/heartbeat/plugins %dir %{_libdir}/heartbeat/plugins/RAExec %dir %{_libdir}/heartbeat/plugins/InterfaceMgr %dir %{_libdir}/heartbeat/plugins/compress %dir %{_libdir}/stonith %dir %{_libdir}/stonith/plugins %dir %{_libdir}/stonith/plugins/stonith2 %dir %{_datadir}/%{name} %{_datadir}/%{name}/ha_cf_support.sh %{_datadir}/%{name}/openais_conf_support.sh %{_datadir}/%{name}/utillib.sh %{_datadir}/%{name}/combine-logs.pl %{_datadir}/%{name}/ha_log.sh %{_sbindir}/ha_logger %{_sbindir}/hb_report %{_sbindir}/lrmadmin %{_sbindir}/cibsecret %{_sbindir}/meatclient %{_sbindir}/stonith %{_sysconfdir}/init.d/logd %doc %{_mandir}/man1/* %doc %{_mandir}/man8/* %doc AUTHORS %doc COPYING %doc ChangeLog %doc logd/logd.cf %doc doc/stonith/README* /sbin/rclogd %{_libdir}/heartbeat/lrmd %{_libdir}/heartbeat/ha_logd %{_libdir}/heartbeat/plugins/RAExec/*.so %{_libdir}/heartbeat/plugins/InterfaceMgr/*.so %{_libdir}/heartbeat/plugins/compress/*.so %{_libdir}/stonith/plugins/external %{_libdir}/stonith/plugins/stonith2/*.so %{_libdir}/stonith/plugins/stonith2/*.py %{_libdir}/stonith/plugins/xen0-ha-dom0-stonith-helper %exclude %{_libdir}/stonith/plugins/external/ssh %exclude %{_libdir}/stonith/plugins/stonith2/null.so %exclude %{_libdir}/stonith/plugins/stonith2/ssh.so %files -n libglue2 %defattr(-,root,root) %{_libdir}/lib*.so.* %doc AUTHORS %doc COPYING.LIB %files -n libglue-devel %defattr(-,root,root) %dir %{_libdir}/heartbeat %dir %{_libdir}/heartbeat/plugins %dir %{_libdir}/heartbeat/plugins/test %dir %{_datadir}/%{name} %{_libdir}/lib*.so %{_libdir}/heartbeat/ipctest %{_libdir}/heartbeat/ipctransientclient %{_libdir}/heartbeat/ipctransientserver %{_libdir}/heartbeat/transient-test.sh %{_libdir}/heartbeat/base64_md5_test %{_libdir}/heartbeat/logtest %{_includedir}/clplumbing %{_includedir}/heartbeat %{_includedir}/stonith %{_includedir}/pils %{_datadir}/%{name}/lrmtest %{_libdir}/heartbeat/plugins/test/test.so %{_libdir}/stonith/plugins/external/ssh %{_libdir}/stonith/plugins/stonith2/null.so %{_libdir}/stonith/plugins/stonith2/ssh.so %doc AUTHORS %doc COPYING %doc COPYING.LIB %changelog Reusable-Cluster-Components-glue--3cff550e1084/config/Makefile.am0000644000000000000000000000147712120057602024573 0ustar00usergroup00000000000000# # Copyright (C) 2005 Guochun Shi (gshi@ncsa.uiuc.edu) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = byteorder_test.c Reusable-Cluster-Components-glue--3cff550e1084/config/byteorder_test.c0000644000000000000000000000033112120057602025725 0ustar00usergroup00000000000000#include int main () { unsigned int a = 0x1234; if ( (unsigned int) ( ((unsigned char *)&a)[0]) == 0x34 ) { printf("little-endian\n"); return 0; } else { printf("big-endian\n"); return 1; } } Reusable-Cluster-Components-glue--3cff550e1084/configure.ac0000644000000000000000000012561412120057602023560 0ustar00usergroup00000000000000dnl dnl autoconf for Pacemaker dnl dnl License: GNU General Public License (GPL) dnl =============================================== dnl Bootstrap dnl =============================================== AC_PREREQ(2.53) dnl Suggested structure: dnl information on the package dnl checks for programs dnl checks for libraries dnl checks for header files dnl checks for types dnl checks for structures dnl checks for compiler characteristics dnl checks for library functions dnl checks for system services AC_INIT(cluster-glue, 1.0.11, linux-ha-dev@lists.linux-ha.org) FEATURES="" HB_PKG=heartbeat AC_CONFIG_AUX_DIR(.) AC_CANONICAL_HOST dnl Where #defines go (e.g. `AC_CHECK_HEADERS' below) dnl dnl Internal header: include/config.h dnl - Contains ALL defines dnl - include/config.h.in is generated automatically by autoheader dnl - NOT to be included in any header files except lha_internal.h dnl (which is also not to be included in any other header files) dnl dnl External header: include/crm_config.h dnl - Contains a subset of defines checked here dnl - Manually edit include/crm_config.h.in to have configure include dnl new defines dnl - Should not include HAVE_* defines dnl - Safe to include anywhere AM_CONFIG_HEADER(include/config.h include/glue_config.h) ALL_LINGUAS="en fr" AC_ARG_WITH(version, [ --with-version=version Override package version (if you're a packager needing to pretend) ], [ PACKAGE_VERSION="$withval" ]) AC_ARG_WITH(pkg-name, [ --with-pkg-name=name Override package name (if you're a packager needing to pretend) ], [ PACKAGE_NAME="$withval" ]) AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION) AC_DEFINE_UNQUOTED(GLUE_VERSION, "$PACKAGE_VERSION", Current version of the glue library) CC_IN_CONFIGURE=yes export CC_IN_CONFIGURE LDD=ldd dnl ======================================================================== dnl Compiler characteristics dnl ======================================================================== AC_PROG_CC dnl Can force other with environment variable "CC". AM_PROG_CC_C_O AC_PROG_CC_STDC AC_LIBTOOL_DLOPEN dnl Enable dlopen support... AC_LIBLTDL_CONVENIENCE dnl make libltdl a convenience lib AC_PROG_LIBTOOL AC_C_STRINGIZE AC_TYPE_SIZE_T AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_CHECK_SIZEOF(clock_t, [], [#include ]) AC_STRUCT_TIMEZONE dnl =============================================== dnl Helpers dnl =============================================== cc_supports_flag() { local CFLAGS="$@" AC_MSG_CHECKING(whether $CC supports "$@") AC_COMPILE_IFELSE([AC_LANG_SOURCE(int main(){return 0;})] ,[RC=0; AC_MSG_RESULT(yes)],[RC=1; AC_MSG_RESULT(no)]) return $RC } dnl =============================================== dnl Configure Options dnl =============================================== dnl Some systems, like Solaris require a custom package name AC_ARG_WITH(pkgname, [ --with-pkgname=name name for pkg (typically for Solaris) ], [ PKGNAME="$withval" ], [ PKGNAME="LXHAhb" ], ) AC_SUBST(PKGNAME) AC_ARG_ENABLE([ansi], [ --enable-ansi force GCC to compile to ANSI/ANSI standard for older compilers. [default=yes]]) AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings very pedantic and fatal warnings for gcc [default=yes]]) AC_ARG_ENABLE([pretty], [ --enable-pretty Pretty-print compiler output unless there is an error [default=no]]) AC_ARG_ENABLE([quiet], [ --enable-quiet Supress make output unless there is an error [default=no]]) AC_ARG_ENABLE([thread-safe], [ --enable-thread-safe Enable some client libraries to be thread safe. [default=no]]) AC_ARG_ENABLE([bundled-ltdl], [ --enable-bundled-ltdl Configure, build and install the standalone ltdl library bundled with ${PACKAGE} [default=no]]) LTDL_LIBS="" AC_ARG_ENABLE([upstart], AS_HELP_STRING([--enable-upstart], [Enable Upstart support in lrmd. [default=no]])) INITDIR="" AC_ARG_WITH(initdir, [ --with-initdir=DIR directory for init (rc) scripts [${INITDIR}]], [ INITDIR="$withval" ]) OCF_ROOT_DIR="/usr/lib/ocf" AC_ARG_WITH(ocf-root, [ --with-ocf-root=DIR directory for OCF scripts [${OCF_ROOT_DIR}]], [ if test x"$withval" = xprefix; then OCF_ROOT_DIR=${prefix}; else OCF_ROOT_DIR="$withval"; fi ]) AC_ARG_WITH( daemon-group, [ --with-daemon-group=GROUP_NAME Group to run our programs as. [default=haclient] ], [ GLUE_DAEMON_GROUP="$withval" ], [ GLUE_DAEMON_GROUP="haclient" ], ) AC_ARG_WITH( daemon-user, [ --with-daemon-user=USER_NAME User to run privileged non-root things as. [default=hacluster] ], [ GLUE_DAEMON_USER="$withval" ], [ GLUE_DAEMON_USER="hacluster" ], ) dnl =============================================== dnl General Processing dnl =============================================== AC_SUBST(HB_PKG) INIT_EXT="" echo Our Host OS: $host_os/$host if test "X$OCF_ROOT_DIR" = X; then OCF_ROOT_DIR="/usr/lib/ocf" fi AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in dnl For consistency with Heartbeat, map NONE->$prefix NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac AC_MSG_NOTICE(Sanitizing INITDIR: ${INITDIR}) case $INITDIR in prefix) INITDIR=$prefix;; "") AC_MSG_CHECKING(which init (rc) directory to use) for initdir in /etc/init.d /etc/rc.d/init.d /sbin/init.d \ /usr/local/etc/rc.d /etc/rc.d do if test -d $initdir then INITDIR=$initdir break fi done AC_MSG_RESULT($INITDIR);; esac AC_SUBST(INITDIR) AC_MSG_NOTICE(Sanitizing libdir: ${libdir}) case $libdir in dnl For consistency with Heartbeat, map NONE->$prefix *prefix*|NONE) AC_MSG_CHECKING(which lib directory to use) for aDir in lib64 lib do trydir="${exec_prefix}/${aDir}" if test -d ${trydir} then libdir=${trydir} break fi done AC_MSG_RESULT($libdir); ;; esac DLOPEN_FORCE_FLAGS="" AC_SUBST(DLOPEN_FORCE_FLAGS) dnl Expand autoconf variables so that we dont end up with '${prefix}' dnl in #defines and python scripts dnl NOTE: Autoconf deliberately leaves them unexpanded to allow dnl make exec_prefix=/foo install dnl No longer being able to do this seems like no great loss to me... eval prefix="`eval echo ${prefix}`" eval exec_prefix="`eval echo ${exec_prefix}`" eval bindir="`eval echo ${bindir}`" eval sbindir="`eval echo ${sbindir}`" eval libexecdir="`eval echo ${libexecdir}`" eval datadir="`eval echo ${datadir}`" eval sysconfdir="`eval echo ${sysconfdir}`" eval sharedstatedir="`eval echo ${sharedstatedir}`" eval localstatedir="`eval echo ${localstatedir}`" eval libdir="`eval echo ${libdir}`" eval includedir="`eval echo ${includedir}`" eval oldincludedir="`eval echo ${oldincludedir}`" eval infodir="`eval echo ${infodir}`" eval mandir="`eval echo ${mandir}`" dnl docdir is a recent addition to autotools eval docdir="`eval echo ${docdir}`" if test "x$docdir" = "x"; then docdir="`eval echo ${datadir}/doc`" fi AC_SUBST(docdir) AC_MSG_CHECKING(for the location of the lock directory) for HA_VARLOCKDIR in ${localstatedir}/lock ${localstatedir}/spool/lock ${localstatedir}/spool/locks ${localstatedir}/lock do if test -d "$HA_VARLOCKDIR" then AC_MSG_RESULT($HA_VARLOCKDIR) break fi done AC_SUBST(HA_VARLOCKDIR) AC_DEFINE_UNQUOTED(HA_VARLOCKDIR,"$HA_VARLOCKDIR", System lock directory) dnl Home-grown variables eval INITDIR="${INITDIR}" for j in prefix exec_prefix bindir sbindir libexecdir datadir sysconfdir \ sharedstatedir localstatedir libdir includedir oldincludedir infodir \ mandir INITDIR docdir HA_VARLOCKDIR do dirname=`eval echo '${'${j}'}'` if test ! -d "$dirname" then AC_MSG_WARN([$j directory ($dirname) does not exist!]) fi done dnl This OS-based decision-making is poor autotools practice; dnl feature-based mechanisms are strongly preferred. dnl dnl So keep this section to a bare minimum; regard as a "necessary evil". ON_LINUX=0 REBOOT_OPTIONS="-f" POWEROFF_OPTIONS="-f" case "$host_os" in *bsd*) LIBS="-L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" INIT_EXT=".sh" ;; *solaris*) REBOOT_OPTIONS="-n" POWEROFF_OPTIONS="-n" ;; *linux*) ON_LINUX=1 REBOOT_OPTIONS="-nf" POWEROFF_OPTIONS="-nf" AC_DEFINE_UNQUOTED(ON_LINUX, $ON_LINUX, Compiling for Linux platform) ;; darwin*) AC_DEFINE_UNQUOTED(ON_DARWIN, 1, Compiling for Darwin platform) LIBS="$LIBS -L${prefix}/lib" CFLAGS="$CFLAGS -I${prefix}/include" ;; esac AM_CONDITIONAL(ON_LINUX, test $ON_LINUX = 1) dnl Eventually remove this dnl CFLAGS="$CFLAGS -I${prefix}/include/heartbeat" AC_SUBST(INIT_EXT) AC_DEFINE_UNQUOTED(HA_LOG_FACILITY, LOG_DAEMON, Default logging facility) AC_MSG_NOTICE(Host CPU: $host_cpu) case "$host_cpu" in ppc64|powerpc64) case $CFLAGS in *powerpc64*) ;; *) if test "$GCC" = yes; then CFLAGS="$CFLAGS -m64" fi ;; esac esac AC_MSG_CHECKING(which format is needed to print uint64_t) case "$host_cpu" in s390x)U64T="%lu";; *64*) U64T="%lu";; *) U64T="%llu";; esac AC_MSG_RESULT($U64T) AC_DEFINE_UNQUOTED(U64T, "$U64T", Correct printf format for logging uint64_t) dnl Variables needed for substitution AC_DEFINE_UNQUOTED(GLUE_DAEMON_USER,"$GLUE_DAEMON_USER", User to run daemons as) AC_SUBST(GLUE_DAEMON_USER) AC_DEFINE_UNQUOTED(GLUE_DAEMON_GROUP,"$GLUE_DAEMON_GROUP", Group to run daemons as) AC_SUBST(GLUE_DAEMON_GROUP) dnl Eventually move out of the heartbeat dir tree and create symlinks when needed GLUE_DAEMON_DIR=$libdir/heartbeat AC_DEFINE_UNQUOTED(GLUE_DAEMON_DIR,"$GLUE_DAEMON_DIR", Location for daemons) AC_SUBST(GLUE_DAEMON_DIR) GLUE_STATE_DIR=${localstatedir}/run AC_DEFINE_UNQUOTED(GLUE_STATE_DIR,"$GLUE_STATE_DIR", Where to keep state files and sockets) AC_SUBST(GLUE_STATE_DIR) GLUE_SHARED_DIR=${datadir}/"$PACKAGE_NAME" AC_DEFINE_UNQUOTED(GLUE_SHARED_DIR,"$GLUE_SHARED_DIR", Location for scripts) AC_SUBST(GLUE_SHARED_DIR) AC_DEFINE_UNQUOTED(HA_VARRUNDIR,"$GLUE_STATE_DIR", Where Heartbeat keeps state files and sockets - old name) AC_SUBST(HA_VARRUNDIR) HA_VARLIBHBDIR=${localstatedir}/lib/heartbeat AC_DEFINE_UNQUOTED(HA_VARLIBHBDIR,"$HA_VARLIBHBDIR", Whatever this used to mean) AC_SUBST(HA_VARLIBHBDIR) AC_DEFINE_UNQUOTED(HA_VARLIBDIR,"$HA_VARLIBHBDIR", Whatever this used to mean) AC_SUBST(HA_VARLIBDIR) AC_DEFINE_UNQUOTED(OCF_ROOT_DIR,"$OCF_ROOT_DIR", OCF root directory - specified by the OCF standard) AC_SUBST(OCF_ROOT_DIR) OCF_RA_DIR="${OCF_ROOT_DIR}/resource.d/" AC_DEFINE_UNQUOTED(OCF_RA_DIR,"$OCF_RA_DIR", Location for OCF RAs) AC_SUBST(OCF_RA_DIR) HA_LOGDAEMON_IPC="${localstatedir}/lib/heartbeat/log_daemon" AC_DEFINE_UNQUOTED(HA_LOGDAEMON_IPC, "$HA_LOGDAEMON_IPC", Logging Daemon IPC socket name) AC_SUBST(HA_LOGDAEMON_IPC) HA_URLBASE="http://linux-ha.org/wiki/" AC_DEFINE_UNQUOTED(HA_URLBASE, "$HA_URLBASE", Web site base URL) AC_SUBST(HA_URLBASE) HA_COREDIR="${localstatedir}/lib/heartbeat/cores" AC_DEFINE_UNQUOTED(HA_COREDIR,"$HA_COREDIR", top directory of area to drop core files in) AC_SUBST(HA_COREDIR) LRM_VARLIBDIR="${localstatedir}/lib/heartbeat/lrm" AC_DEFINE_UNQUOTED(LRM_VARLIBDIR,"$LRM_VARLIBDIR", LRM directory) AC_SUBST(LRM_VARLIBDIR) LRM_CIBSECRETS="${localstatedir}/lib/heartbeat/lrm/secrets" AC_DEFINE_UNQUOTED(LRM_CIBSECRETS,"$LRM_CIBSECRETS", CIB secrets location) AC_SUBST(LRM_CIBSECRETS) AC_DEFINE_UNQUOTED(PILS_BASE_PLUGINDIR,"$libdir/heartbeat/plugins", Default plugin search path) AC_DEFINE_UNQUOTED(HA_PLUGIN_DIR,"$libdir/heartbeat/plugins", Where to find plugins) AC_DEFINE_UNQUOTED(LRM_PLUGIN_DIR,"$libdir/heartbeat/plugins/RAExec", Where to find LRM plugins) AC_DEFINE_UNQUOTED(LSB_RA_DIR,"$INITDIR", Location for LSB RAs) LSB_RA_DIR=$INITDIR AC_SUBST(LSB_RA_DIR) AC_DEFINE_UNQUOTED(HA_SYSCONFDIR, "$sysconfdir", Location of system configuration files) HA_HBCONF_DIR=${sysconfdir}/ha.d/ AC_DEFINE_UNQUOTED(HA_HBCONF_DIR,"$HA_HBCONF_DIR", Location for v1 Heartbeat configuration) AC_SUBST(HA_HBCONF_DIR) HB_RA_DIR=${sysconfdir}/ha.d/resource.d/ AC_DEFINE_UNQUOTED(HB_RA_DIR,"$HB_RA_DIR", Location for v1 Heartbeat RAs) AC_SUBST(HB_RA_DIR) stonith_plugindir="${libdir}/stonith/plugins" stonith_ext_plugindir="${stonith_plugindir}/external" stonith_rhcs_plugindir="${stonith_plugindir}/rhcs" AC_DEFINE_UNQUOTED(ST_TEXTDOMAIN, "stonith", Stonith plugin domain) AC_DEFINE_UNQUOTED(STONITH_MODULES, "$stonith_plugindir", Location of stonith plugins) AC_DEFINE_UNQUOTED(STONITH_EXT_PLUGINDIR, "$stonith_ext_plugindir", Location of non-plugin stonith scripts) AC_DEFINE_UNQUOTED(STONITH_RHCS_PLUGINDIR, "$stonith_rhcs_plugindir", Location of RHCS fence scripts) AC_SUBST(stonith_plugindir) AC_SUBST(stonith_ext_plugindir) AC_SUBST(stonith_rhcs_plugindir) dnl Old names for new things AC_DEFINE_UNQUOTED(HA_CCMUSER, "$GLUE_DAEMON_USER", User to run daemons as) AC_DEFINE_UNQUOTED(HA_APIGROUP, "$GLUE_DAEMON_GROUP", Group to run daemons as) AC_DEFINE_UNQUOTED(HA_LIBHBDIR, "$GLUE_DAEMON_DIR", Location for daemons) LRM_DIR=lrm AC_SUBST(LRM_DIR) AC_PATH_PROGS(HG, hg false) AC_MSG_CHECKING(build version) GLUE_BUILD_VERSION=unknown if test -f $srcdir/.hg_archival.txt; then GLUE_BUILD_VERSION=`cat $srcdir/.hg_archival.txt | awk '/node:/ { print $2 }'` elif test -x $HG -a -d .hg; then GLUE_BUILD_VERSION=`$HG id -itb` if test $? != 0; then GLUE_BUILD_VERSION=unknown fi fi AC_DEFINE_UNQUOTED(GLUE_BUILD_VERSION, "$GLUE_BUILD_VERSION", Build version) AC_MSG_RESULT($GLUE_BUILD_VERSION) AC_SUBST(GLUE_BUILD_VERSION) dnl check byte order AC_MSG_CHECKING(for byteorder) AC_C_BIGENDIAN( [AC_MSG_RESULT(big-endian); AC_DEFINE(CONFIG_BIG_ENDIAN, 1, [big-endian])], [AC_MSG_RESULT(little-endian); AC_DEFINE(CONFIG_LITTLE_ENDIAN, 1, [little-endian])], ) dnl =============================================== dnl Program Paths dnl =============================================== PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" export PATH dnl Replacing AC_PROG_LIBTOOL with AC_CHECK_PROG because LIBTOOL dnl was NOT being expanded all the time thus causing things to fail. AC_CHECK_PROGS(LIBTOOL, glibtool libtool libtool15 libtool13) AM_PATH_PYTHON AC_CHECK_PROGS(MAKE, gmake make) AC_PATH_PROGS(HTML2TXT, lynx w3m) AC_PATH_PROGS(HELP2MAN, help2man) AC_PATH_PROGS(POD2MAN, pod2man, pod2man) AC_PATH_PROGS(SSH, ssh, /usr/bin/ssh) AC_PATH_PROGS(SCP, scp, /usr/bin/scp) AC_PATH_PROGS(HG, hg, /bin/false) AC_PATH_PROGS(TAR, tar) AC_PATH_PROGS(MD5, md5) AC_PATH_PROGS(RPM, rpm) AC_PATH_PROGS(TEST, test) AC_PATH_PROGS(PING, ping, /bin/ping) AC_PATH_PROGS(IFCONFIG, ifconfig, /sbin/ifconfig) AC_PATH_PROGS(MAILCMD, mailx mail) AC_PATH_PROGS(EGREP, egrep) AC_PATH_PROGS(PKGCONFIG, pkg-config) AC_PATH_PROGS(XML2CONFIG, xml2-config) AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [build documentation (default is yes)]), [], [enable_doc=yes]) if test "x$enable_doc" != "xno"; then AC_PATH_PROGS(XSLTPROC, xsltproc) if test "x$XSLTPROC" = "x"; then AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages]) fi AC_PATH_PROGS(ASCIIDOC, asciidoc) if test "x$ASCIIDOC" = "x"; then AC_MSG_WARN([asciidoc not installed, unable to (re-)build manual pages]) fi fi AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" ) AC_PATH_PROGS(VALGRIND_BIN, valgrind, /usr/bin/valgrind) AC_DEFINE_UNQUOTED(VALGRIND_BIN, "$VALGRIND_BIN", Valgrind command) AC_SUBST(MAILCMD) AC_SUBST(EGREP) AC_SUBST(SHELL) AC_SUBST(PING) AC_SUBST(TEST) AC_SUBST(RPM) AC_SUBST(XSLTPROC) AC_MSG_CHECKING(ifconfig option to list interfaces) for IFCONFIG_A_OPT in "-A" "-a" "" do $IFCONFIG $IFCONFIG_A_OPT > /dev/null 2>&1 if test "$?" = 0 then AC_DEFINE_UNQUOTED(IFCONFIG_A_OPT, "$IFCONFIG_A_OPT", option for ifconfig command) AC_MSG_RESULT($IFCONFIG_A_OPT) break fi done AC_SUBST(IFCONFIG_A_OPT) if test x"${LIBTOOL}" = x""; then AC_MSG_ERROR(You need (g)libtool installed in order to build ${PACKAGE}) fi if test x"${MAKE}" = x""; then AC_MSG_ERROR(You need (g)make installed in order to build ${PACKAGE}) fi AM_CONDITIONAL(BUILD_HELP, test x"${HELP2MAN}" != x"") if test x"${HELP2MAN}" != x""; then FEATURES="$FEATURES manpages" fi dnl =============================================== dnl Libraries dnl =============================================== AC_CHECK_LIB(socket, socket) AC_CHECK_LIB(c, dlopen) dnl if dlopen is in libc... AC_CHECK_LIB(dl, dlopen) dnl for Linux AC_CHECK_LIB(rt, sched_getscheduler) dnl for Tru64 AC_CHECK_LIB(gnugetopt, getopt_long) dnl if available AC_CHECK_LIB(uuid, uuid_parse) dnl e2fsprogs AC_CHECK_LIB(uuid, uuid_create) dnl ossp AC_CHECK_LIB(posix4, sched_getscheduler) if test x"${PKGCONFIG}" = x""; then AC_MSG_ERROR(You need pkgconfig installed in order to build ${PACKAGE}) fi dnl dnl On many systems libcrypto is needed when linking against libsnmp. dnl Check to see if it exists, and if so use it. dnl AC_CHECK_LIB(crypto, CRYPTO_free, CRYPTOLIB="-lcrypto",) AC_SUBST(CRYPTOLIB) if test "x${enable_thread_safe}" = "xyes"; then GPKGNAME="gthread-2.0" else GPKGNAME="glib-2.0" fi if $PKGCONFIG --exists $GPKGNAME then GLIBCONFIG="$PKGCONFIG $GPKGNAME" else set -x echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH $PKGCONFIG --exists $GPKGNAME; echo $? $PKGCONFIG --cflags $GPKGNAME; echo $? $PKGCONFIG $GPKGNAME; echo $? set +x AC_MSG_ERROR(You need glib2-devel installed in order to build ${PACKAGE}) fi AC_MSG_RESULT(using $GLIBCONFIG) # # Where is dlopen? # if test "$ac_cv_lib_c_dlopen" = yes; then LIBADD_DL="" elif test "$ac_cv_lib_dl_dlopen" = yes; then LIBADD_DL=-ldl else LIBADD_DL=${lt_cv_dlopen_libs} fi dnl dnl Check for location of gettext dnl dnl On at least Solaris 2.x, where it is in libc, specifying lintl causes dnl grief. Ensure minimal result, not the sum of all possibilities. dnl And do libc first. dnl Known examples: dnl c: Linux, Solaris 2.6+ dnl intl: BSD, AIX AC_CHECK_LIB(c, gettext) if test x$ac_cv_lib_c_gettext != xyes; then AC_CHECK_LIB(intl, gettext) fi if test x$ac_cv_lib_c_gettext != xyes -a x$ac_cv_lib_intl_gettext != xyes; then AC_MSG_ERROR(You need gettext installed in order to build ${PACKAGE}) fi if test "X$GLIBCONFIG" != X; then AC_MSG_CHECKING(for special glib includes: ) GLIBHEAD=`$GLIBCONFIG --cflags` AC_MSG_RESULT($GLIBHEAD) CPPFLAGS="$CPPFLAGS $GLIBHEAD" AC_MSG_CHECKING(for glib library flags) GLIBLIB=`$GLIBCONFIG --libs` AC_MSG_RESULT($GLIBLIB) LIBS="$LIBS $GLIBLIB" fi dnl ======================================================================== dnl Headers dnl ======================================================================== AC_HEADER_STDC AC_CHECK_HEADERS(arpa/inet.h) AC_CHECK_HEADERS(asm/types.h) AC_CHECK_HEADERS(assert.h) AC_CHECK_HEADERS(auth-client.h) AC_CHECK_HEADERS(ctype.h) AC_CHECK_HEADERS(dirent.h) AC_CHECK_HEADERS(errno.h) AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(getopt.h) AC_CHECK_HEADERS(glib.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(limits.h) AC_CHECK_HEADERS(linux/errqueue.h,,, [#ifdef HAVE_LINUX_TYPES_H # include #endif ]) AC_CHECK_HEADERS(malloc.h) AC_CHECK_HEADERS(netdb.h) AC_CHECK_HEADERS(netinet/in.h) AC_CHECK_HEADERS(netinet/ip.h) AC_CHECK_HEADERS(pthread.h) AC_CHECK_HEADERS(pwd.h) AC_CHECK_HEADERS(sgtty.h) AC_CHECK_HEADERS(signal.h) AC_CHECK_HEADERS(stdarg.h) AC_CHECK_HEADERS(stddef.h) AC_CHECK_HEADERS(stdio.h) AC_CHECK_HEADERS(stdlib.h) AC_CHECK_HEADERS(string.h) AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sys/dir.h) AC_CHECK_HEADERS(sys/ioctl.h) AC_CHECK_HEADERS(sys/param.h) AC_CHECK_HEADERS(sys/poll.h) AC_CHECK_HEADERS(sys/reboot.h) AC_CHECK_HEADERS(sys/resource.h) AC_CHECK_HEADERS(sys/select.h) AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(sys/sockio.h) AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/timeb.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(sys/uio.h) AC_CHECK_HEADERS(sys/un.h) AC_CHECK_HEADERS(sys/utsname.h) AC_CHECK_HEADERS(sys/wait.h) AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(winsock.h) AC_CHECK_HEADERS(sys/termios.h) AC_CHECK_HEADERS(termios.h) dnl These headers need prerequisits before the tests will pass dnl AC_CHECK_HEADERS(net/if.h) dnl AC_CHECK_HEADERS(netinet/icmp6.h) dnl AC_CHECK_HEADERS(netinet/ip6.h) dnl AC_CHECK_HEADERS(netinet/ip_icmp.h) AC_MSG_CHECKING(for special libxml2 includes) if test "x$XML2CONFIG" = "x"; then AC_MSG_ERROR(libxml2 config not found) else XML2HEAD="`$XML2CONFIG --cflags`" AC_MSG_RESULT($XML2HEAD) AC_CHECK_LIB(xml2, xmlReadMemory) fi CPPFLAGS="$CPPFLAGS $XML2HEAD" AC_CHECK_HEADERS(libxml/xpath.h) if test "$ac_cv_header_libxml_xpath_h" != "yes"; then AC_MSG_ERROR(The libxml developement headers were not found) fi dnl Check syslog.h for 'facilitynames' table dnl AC_CACHE_CHECK([for facilitynames in syslog.h],ac_cv_HAVE_SYSLOG_FACILITYNAMES,[ AC_TRY_COMPILE([ #define SYSLOG_NAMES #include #include ], [ void *fnames; fnames = facilitynames; ], ac_cv_HAVE_SYSLOG_FACILITYNAMES=yes,ac_cv_HAVE_SYSLOG_FACILITYNAMES=no,ac_cv_HAVE_SYSLOG_FACILITYNAMES=cross)]) if test x"$ac_cv_HAVE_SYSLOG_FACILITYNAMES" = x"yes"; then AC_DEFINE(HAVE_SYSLOG_FACILITYNAMES,1,[ ]) fi dnl Check for POSIX signals dnl AC_CACHE_CHECK([have POSIX signals],ac_cv_HAVE_POSIX_SIGNALS,[ AC_TRY_COMPILE([ #include ], [ struct sigaction act, oact; sigaction(0, &act, &oact); return 0;], ac_cv_HAVE_POSIX_SIGNALS=yes,ac_cv_HAVE_POSIX_SIGNALS=no,ac_cv_HAVE_POSIX_SIGNALS=cross)]) if test x"$ac_cv_HAVE_POSIX_SIGNALS" = x"yes"; then AC_DEFINE(HAVE_POSIX_SIGNALS,1,[ ]) fi dnl 'reboot()' system call: one argument (e.g. Linux) or two (e.g. Solaris)? dnl AC_CACHE_CHECK([number of arguments in reboot system call], ac_cv_REBOOT_ARGS,[ AC_TRY_COMPILE( [#include ], [(void)reboot(0);], ac_cv_REBOOT_ARGS=1, [AC_TRY_COMPILE( [#include ], [(void)reboot(0,(void *)0);], ac_cv_REBOOT_ARGS=2, ac_cv_REBOOT_ARGS=0 )], ac_cv_REBOOT_ARGS=0 ) ] ) dnl Argument count of 0 suggests no known 'reboot()' call. if test "$ac_cv_REBOOT_ARGS" -ge "1"; then AC_DEFINE_UNQUOTED(REBOOT_ARGS,$ac_cv_REBOOT_ARGS,[number of arguments for reboot system call]) fi AC_PATH_PROGS(REBOOT, reboot, /sbin/reboot) AC_SUBST(REBOOT) AC_SUBST(REBOOT_OPTIONS) AC_DEFINE_UNQUOTED(REBOOT, "$REBOOT", path to the reboot command) AC_DEFINE_UNQUOTED(REBOOT_OPTIONS, "$REBOOT_OPTIONS", reboot options) AC_PATH_PROGS(POWEROFF_CMD, poweroff, /sbin/poweroff) AC_SUBST(POWEROFF_CMD) AC_SUBST(POWEROFF_OPTIONS) AC_DEFINE_UNQUOTED(POWEROFF_CMD, "$POWEROFF_CMD", path to the poweroff command) AC_DEFINE_UNQUOTED(POWEROFF_OPTIONS, "$POWEROFF_OPTIONS", poweroff options) dnl Sockets are our preferred and supported comms mechanism. But the dnl implementation needs to be able to convey credentials: some don't. dnl So on a few OSes, credentials-carrying streams might be a better choice. dnl dnl Solaris releases up to and including "9" fall into this category dnl (its sockets don't carry credentials; streams do). dnl dnl At Solaris 10, "getpeerucred()" is available, for both sockets and dnl streams, so it should probably use (preferred) socket mechanism. AC_CHECK_HEADERS(stropts.h) dnl streams available (fallback option) AC_CHECK_HEADERS(ucred.h) dnl e.g. Solaris 10 decl. of "getpeerucred()" AC_CHECK_FUNCS(getpeerucred) dnl ************************************************************************ dnl checks for headers needed by clplumbing On BSD AC_CHECK_HEADERS(sys/syslimits.h) if test "$ac_cv_header_sys_param_h" = no; then AC_CHECK_HEADERS(sys/ucred.h) else AC_CHECK_HEADERS(sys/ucred.h,[],[],[#include ]) fi dnl ************************************************************************ dnl checks for headers needed by clplumbing On Solaris AC_CHECK_HEADERS(sys/cred.h xti.h) dnl ************************************************************************ dnl checks for headers needed by clplumbing On FreeBSD/Solaris AC_CHECK_HEADERS(sys/filio.h) dnl ======================================================================== dnl Structures dnl ======================================================================== AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[[#include ]]) AC_CHECK_TYPES([nfds_t],,,[[#include ]]) AC_MSG_CHECKING(if clock_t is long enough) if test $ac_cv_sizeof_clock_t -ge 8; then AC_MSG_RESULT(yes) AC_DEFINE(CLOCK_T_IS_LONG_ENOUGH, 1, [Set if CLOCK_T is adequate by itself for the "indefinite future" (>= 100 years)]) else AC_MSG_RESULT(no) fi dnl ======================================================================== dnl Functions dnl ======================================================================== AC_CHECK_FUNCS(g_log_set_default_handler) AC_CHECK_FUNCS(getopt, AC_DEFINE(HAVE_DECL_GETOPT, 1, [Have getopt function])) AC_CHECK_FUNCS(getpeereid) dnl ********************************************************************** dnl Check for various argv[] replacing functions on various OSs dnl dnl Borrowed from Proftpd dnl Proftpd is Licenced under the terms of the GNU General Public Licence dnl and is available from http://www.proftpd.org/ dnl AC_CHECK_FUNCS(setproctitle) AC_CHECK_HEADERS(libutil.h) AC_CHECK_LIB(util, setproctitle, [AC_DEFINE(HAVE_SETPROCTITLE,1,[ ]) ac_cv_func_setproctitle="yes" ; LIBS="$LIBS -lutil"]) if test "$ac_cv_func_setproctitle" = "yes"; then pf_argv_set="PF_ARGV_NONE" fi if test "$pf_argv_set" = ""; then AC_CHECK_HEADERS(sys/pstat.h) if test "$ac_cv_header_pstat_h" = "yes"; then AC_CHECK_FUNCS(pstat) if test "$ac_cv_func_pstat" = "yes"; then pf_argv_set="PF_ARGV_PSTAT" else pf_argv_set="PF_ARGV_WRITEABLE" fi fi if test "$pf_argv_set" = ""; then AC_EGREP_HEADER([#define.*PS_STRINGS.*],sys/exec.h, have_psstrings="yes",have_psstrings="no") if test "$have_psstrings" = "yes"; then pf_argv_set="PF_ARGV_PSSTRINGS" fi fi if test "$pf_argv_set" = ""; then AC_CACHE_CHECK(whether __progname and __progname_full are available, pf_cv_var_progname, AC_TRY_LINK([extern char *__progname, *__progname_full;], [__progname = "foo"; __progname_full = "foo bar";], pf_cv_var_progname="yes", pf_cv_var_progname="no")) if test "$pf_cv_var_progname" = "yes"; then AC_DEFINE(HAVE___PROGNAME,1,[ ]) fi AC_CACHE_CHECK(which argv replacement method to use, pf_cv_argv_type, AC_EGREP_CPP(yes,[ #if defined(__GNU_HURD__) yes #endif ],pf_cv_argv_type="new", pf_cv_argv_type="writeable")) if test "$pf_cv_argv_type" = "new"; then pf_argv_set="PF_ARGV_NEW" fi if test "$pf_argv_set" = ""; then pf_argv_set="PF_ARGV_WRITEABLE" fi fi fi AC_DEFINE_UNQUOTED(PF_ARGV_TYPE, $pf_argv_set, mechanism to pretty-print ps output: setproctitle-equivalent) dnl End of tests borrowed from Proftpd dnl ======================================================================== dnl ltdl dnl ======================================================================== AC_CHECK_LIB(ltdl, lt_dlopen, [LTDL_foo=1]) if test "x${enable_bundled_ltdl}" = "xyes"; then if test $ac_cv_lib_ltdl_lt_dlopen = yes; then AC_MSG_NOTICE([Disabling usage of installed ltdl]) fi ac_cv_lib_ltdl_lt_dlopen=no fi LIBLTDL_DIR="" if test $ac_cv_lib_ltdl_lt_dlopen != yes ; then AC_MSG_NOTICE([Installing local ltdl]) LIBLTDL_DIR=libltdl ( cd $srcdir ; $TAR -xvf libltdl.tar ) if test "$?" -ne 0; then AC_MSG_ERROR([$TAR of libltdl.tar in $srcdir failed]) fi AC_CONFIG_SUBDIRS(libltdl) else LIBS="$LIBS -lltdl" AC_MSG_NOTICE([Using installed ltdl]) INCLTDL="" LIBLTDL="" fi AC_SUBST(INCLTDL) AC_SUBST(LIBLTDL) AC_SUBST(LIBLTDL_DIR) dnl ======================================================================== dnl libnet dnl ======================================================================== AC_ARG_ENABLE([libnet], [ --enable-libnet Use libnet for ARP based funcationality, [default=try]], [], [enable_libnet=try]) libnet="" libnet_version="none" LIBNETLIBS="" LIBNETDEFINES="" AC_MSG_CHECKING(if libnet is required) libnet_fatal=$enable_libnet case $enable_libnet in no) ;; yes|libnet10|libnet11|10|11) libnet_fatal=yes;; try) case $host_os in *Linux*|*linux*) libnet_fatal=no;; *) libnet_fatal=yes;; dnl legacy behavior esac ;; *) libnet_fatal=yes; enable_libnet=try;; esac AC_MSG_RESULT($libnet_fatal) if test "x$enable_libnet" != "xno"; then AC_PATH_PROGS(LIBNETCONFIG, libnet-config) AC_CHECK_LIB(nsl, t_open) dnl -lnsl AC_CHECK_LIB(socket, socket) dnl -lsocket AC_CHECK_LIB(net, libnet_get_hwaddr, LIBNETLIBS=" -lnet", []) fi AC_MSG_CHECKING(for libnet) if test "x$LIBNETLIBS" != "x" -o "x$enable_libnet" = "xlibnet11"; then LIBNETDEFINES="" if test "$ac_cv_lib_nsl_t_open" = yes; then LIBNETLIBS="-lnsl $LIBNETLIBS" fi if test "$ac_cv_lib_socket_socket" = yes; then LIBNETLIBS="-lsocket $LIBNETLIBS" fi libnet=net libnet_version="libnet1.1" fi if test "x$enable_libnet" = "xtry" -o "x$enable_libnet" = "xlibnet10"; then if test "x$LIBNETLIBS" = x -a "x${LIBNETCONFIG}" != "x" ; then LIBNETDEFINES="`$LIBNETCONFIG --defines` `$LIBNETCONFIG --cflags`"; LIBNETLIBS="`$LIBNETCONFIG --libs`"; libnet_version="libnet1.0 (old)" case $LIBNETLIBS in *-l*) libnet=`echo $LIBNETLIBS | sed 's%.*-l%%'`;; *) libnet_version=none;; esac CPPFLAGS="$CPPFLAGS $LIBNETDEFINES" AC_CHECK_HEADERS(libnet.h) if test "$ac_cv_header_libnet_h" = no; then libnet_version=none fi fi fi AC_MSG_RESULT(found $libnet_version) if test "$libnet_version" = none; then LIBNETLIBS="" LIBNETDEFINES="" if test $libnet_fatal = yes; then AC_MSG_ERROR(libnet not found) fi else AC_CHECK_LIB($libnet,libnet_init, [new_libnet=yes; AC_DEFINE(HAVE_LIBNET_1_1_API, 1, Libnet 1.1 API)], [new_libnet=no; AC_DEFINE(HAVE_LIBNET_1_0_API, 1, Libnet 1.0 API)],$LIBNETLIBS) fi dnl ************************************************************************ dnl * Check for netinet/icmp6.h to enable the IPv6addr resource agent AC_CHECK_HEADERS(netinet/icmp6.h,[],[],[#include ]) AM_CONDITIONAL(USE_IPV6ADDR, test "$ac_cv_header_netinet_icmp6_h" = yes -a "$new_libnet" = yes ) dnl ======================================================================== dnl SNMP dnl ======================================================================== SNMPLIB="" SNMPCONFIG="" ENABLE_SNMP="yes" if test "x${enable_snmp}" = "xno"; then ENABLE_SNMP="no" fi AC_CHECK_HEADERS(ucd-snmp/snmp.h,[],[],[#include #include ]) AC_CHECK_HEADERS(net-snmp/net-snmp-config.h) if test "x${ENABLE_SNMP}" = "xno"; then # nothing : elif test "x${ac_cv_header_net_snmp_net_snmp_config_h}" = "xyes"; then AC_PATH_PROGS(SNMPCONFIG, net-snmp-config) if test "X${SNMPCONFIG}" = "X"; then AC_MSG_RESULT(You need the net_snmp development package to continue.) ENABLE_SNMP="no" else AC_MSG_CHECKING(for special snmp libraries) SNMPLIB=`${SNMPCONFIG} --libs` AC_MSG_RESULT($SNMPLIB) fi elif test "x${ac_cv_header_ucd_snmp_snmp_h}" = "xyes"; then # UCD SNMP # ucd-snmp-config does not seem to exist, so just # rely on people having their LDFLAGS set to the path where AC_CHECK_LIB(snmp, init_snmp, SNMPLIB="-lsnmp") if test "X${SNMPLIB}" = "X"; then AC_CHECK_LIB(ucdsnmp, init_snmp, SNMPLIB="-lucdsnmp") fi if test "X${SNMPLIB}" = "X"; then ENABLE_SNMP="no" AC_MSG_RESULT("Could not find ucdsnmp libary." "Please make sure that libsnmp or libucdsnmp" "are in your library path. Or the path to LDFLAGS") fi else ENABLE_SNMP="no" fi AC_SUBST(SNMPLIB) dnl ======================================================================== dnl Stonith Devices dnl ======================================================================== if test "x${enable_ipmilan}" = "x"; then enable_ipmilan="yes" fi if test "x${enable_ipmilan}" = "xyes" -o "x${enable_ipmilan}" = "xtry"; then AC_MSG_CHECKING(For libOpenIPMI version 1.4 or greater) AC_TRY_COMPILE([#include ], [ #if (OPENIPMI_VERSION_MAJOR == 1) && (OPENIPMI_VERSION_MINOR < 4) #error "Too Old" #endif ], AC_MSG_RESULT("yes"); enable_ipmilan="yes", AC_MSG_RESULT("no"); enable_ipmilan="no") else enable_ipmilan="no" fi AC_CHECK_HEADERS(curl/curl.h) AC_CHECK_HEADERS(openhpi/SaHpi.h) AC_CHECK_HEADERS(vacmclient_api.h) AM_CONDITIONAL(USE_APC_SNMP, test "$ENABLE_SNMP" = "yes") AM_CONDITIONAL(USE_VACM, test "$ac_cv_header_vacmclient_api_h" = yes) AM_CONDITIONAL(USE_DRAC3, test "$ac_cv_header_curl_curl_h" = yes -a "$ac_cv_header_libxml_xpath_h" = yes) AM_CONDITIONAL(USE_OPENHPI, test "$ac_cv_header_openhpi_SaHpi_h" = yes && pkg-config --atleast-version 2.6 openhpi) AM_CONDITIONAL(IPMILAN_BUILD, test "X$enable_ipmilan" = "Xyes") dnl ======================================================================== dnl ZLIB and BZ2 dnl ======================================================================== dnl check if header file and lib are there for zlib zlib_installed="yes" AC_CHECK_HEADERS(zlib.h, , [zlib_installed="no"],) AC_CHECK_LIB(z, compress , , [zlib_installed="no"]) AM_CONDITIONAL(BUILD_ZLIB_COMPRESS_MODULE, test "x${zlib_installed}" = "xyes") if test "x${zlib_installed}" = "xno"; then FatalMissingThing "zlib" \ "The zlib library is missing" fi bz2_installed="yes" AC_CHECK_HEADERS(bzlib.h, , [bz2_installed="no"],) AC_CHECK_LIB(bz2, BZ2_bzBuffToBuffCompress , , [bz2_installed="no"]) AM_CONDITIONAL(BUILD_BZ2_COMPRESS_MODULE, test "x${bz2_installed}" = "xyes") #if test x$ac_cv_lib_bz2_BZ2_bzBuffToBuffCompress != xyes ; then # AC_MSG_ERROR(BZ2 libraries not found) #fi if test x$ac_cv_header_bzlib_h != xyes; then AC_MSG_ERROR(BZ2 Development headers not found) fi dnl ======================================================================== dnl Upstart via DBus dnl ======================================================================== if test x$enable_upstart = xyes; then PKG_CHECK_MODULES(DBUS, [dbus-1, dbus-glib-1]) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) AC_PATH_PROGS(DBUS_BINDING_TOOL, dbus-binding-tool) fi AM_CONDITIONAL(UPSTART, test x$enable_upstart = xyes) dnl ======================================================================== dnl checks for library functions to replace them dnl dnl NoSuchFunctionName: dnl is a dummy function which no system supplies. It is here to make dnl the system compile semi-correctly on OpenBSD which doesn't know dnl how to create an empty archive dnl dnl scandir: Only on BSD. dnl System-V systems may have it, but hidden and/or deprecated. dnl A replacement function is supplied for it. dnl dnl setenv: is some bsdish function that should also be avoided (use dnl putenv instead) dnl On the other hand, putenv doesn't provide the right API for the dnl code and has memory leaks designed in (sigh...) Fortunately this dnl A replacement function is supplied for it. dnl dnl strerror: returns a string that corresponds to an errno. dnl A replacement function is supplied for it. dnl dnl unsetenv: is some bsdish function that should also be avoided (No dnl replacement) dnl A replacement function is supplied for it. dnl dnl strnlen: is a gnu function similar to strlen, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl strndup: is a gnu function similar to strdup, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl daemon: is a GNU function. The daemon() function is for programs wishing to dnl detach themselves from the controlling terminal and run in the dnl background as system daemon dnl A replacement function is supplied for it. AC_REPLACE_FUNCS(alphasort inet_pton NoSuchFunctionName scandir setenv strerror unsetenv strnlen strndup daemon strlcpy strlcat) dnl ======================================================================== dnl Compiler flags dnl ======================================================================== dnl Make sure that CFLAGS is not exported. If the user did dnl not have CFLAGS in their environment then this should have dnl no effect. However if CFLAGS was exported from the user's dnl environment, then the new CFLAGS will also be exported dnl to sub processes. CC_ERRORS="" CC_EXTRAS="" if export | fgrep " CFLAGS=" > /dev/null; then SAVED_CFLAGS="$CFLAGS" unset CFLAGS CFLAGS="$SAVED_CFLAGS" unset SAVED_CFLAGS fi if test "$GCC" != yes; then CFLAGS="$CFLAGS -g" enable_fatal_warnings=no else CFLAGS="$CFLAGS -ggdb3 -O0" # We had to eliminate -Wnested-externs because of libtool changes EXTRA_FLAGS="-fgnu89-inline -fstack-protector-all -Wall -Waggregate-return -Wbad-function-cast -Wcast-qual -Wcast-align -Wdeclaration-after-statement -Wendif-labels -Wfloat-equal -Wformat=2 -Wformat-security -Wformat-nonliteral -Winline -Wmissing-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wnested-externs -Wno-long-long -Wno-strict-aliasing -Wpointer-arith -Wstrict-prototypes -Wunsigned-char -Wwrite-strings" # Additional warnings it might be nice to enable one day # -Wshadow # -Wunreachable-code for j in $EXTRA_FLAGS do if cc_supports_flag $j then CC_EXTRAS="$CC_EXTRAS $j" fi done dnl In lib/ais/Makefile.am there's a gcc option available as of v4.x GCC_MAJOR=`gcc -v 2>&1 | awk 'END{print $3}' | sed 's/[.].*//'` AM_CONDITIONAL(GCC_4, test "${GCC_MAJOR}" = 4) dnl System specific options case "$host_os" in *linux*|*bsd*) if test "${enable_fatal_warnings}" = "unknown"; then enable_fatal_warnings=yes fi ;; esac if test "x${enable_fatal_warnings}" != xno && cc_supports_flag -Werror ; then enable_fatal_warnings=yes else enable_fatal_warnings=no fi if test "x${enable_ansi}" != xno && cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE(Enabling ANSI Compatibility) CC_EXTRAS="$CC_EXTRAS -ansi -D_GNU_SOURCE -DANSI_ONLY" fi AC_MSG_NOTICE(Activated additional gcc flags: ${CC_EXTRAS}) fi CFLAGS="$CFLAGS $CC_EXTRAS" NON_FATAL_CFLAGS="$CFLAGS" AC_SUBST(NON_FATAL_CFLAGS) dnl dnl We reset CFLAGS to include our warnings *after* all function dnl checking goes on, so that our warning flags don't keep the dnl AC_*FUNCS() calls above from working. In particular, -Werror will dnl *always* cause us troubles if we set it before here. dnl dnl if test "x${enable_fatal_warnings}" = xyes ; then AC_MSG_NOTICE(Enabling Fatal Warnings) CFLAGS="$CFLAGS -Werror" fi AC_SUBST(CFLAGS) dnl This is useful for use in Makefiles that need to remove one specific flag CFLAGS_COPY="$CFLAGS" AC_SUBST(CFLAGS_COPY) AC_SUBST(LIBADD_DL) dnl extra flags for dynamic linking libraries AC_SUBST(LIBADD_INTL) dnl extra flags for GNU gettext stuff... AC_SUBST(LOCALE) dnl Options for cleaning up the compiler output PRETTY_CC="" QUIET_LIBTOOL_OPTS="" QUIET_MAKE_OPTS="" if test x"${enable_pretty}" = "xyes"; then enable_quiet="yes" echo "install_sh: ${install_sh}" PRETTY_CC="`pwd`/tools/ccdv" dnl It would be nice if this was rebuilt when needed too... mkdir `pwd`/tools/ 2>/dev/null ${CC} $CFLAGS -o `pwd`/tools/ccdv ${srcdir}/tools/ccdv.c CC="\$(PRETTY_CC) ${CC}" fi if test "x${enable_quiet}" = "xyes"; then QUIET_LIBTOOL_OPTS="--quiet" QUIET_MAKE_OPTS="--quiet" fi AC_MSG_RESULT(Supress make details: ${enable_quiet}) AC_MSG_RESULT(Pretty print compiler output: ${enable_pretty}) dnl Put the above variables to use LIBTOOL="${LIBTOOL} --tag=CC \$(QUIET_LIBTOOL_OPTS)" MAKE="${MAKE} \$(QUIET_MAKE_OPTS)" AC_SUBST(CC) AC_SUBST(MAKE) AC_SUBST(LIBTOOL) AC_SUBST(PRETTY_CC) AC_SUBST(QUIET_MAKE_OPTS) AC_SUBST(QUIET_LIBTOOL_OPTS) dnl The Makefiles and shell scripts we output AC_CONFIG_FILES(Makefile \ config/Makefile \ include/Makefile \ include/pils/Makefile \ include/pils/plugin.h \ include/clplumbing/Makefile \ include/lrm/Makefile \ include/stonith/Makefile \ lib/Makefile \ lib/pils/Makefile \ lib/clplumbing/Makefile \ lib/stonith/Makefile \ lib/lrm/Makefile \ lib/plugins/Makefile \ lib/plugins/InterfaceMgr/Makefile \ lib/plugins/compress/Makefile \ lib/plugins/lrm/Makefile \ lib/plugins/lrm/dbus/Makefile \ lib/plugins/stonith/Makefile \ lib/plugins/stonith/ribcl.py \ lib/plugins/stonith/external/Makefile \ lib/plugins/stonith/external/drac5 \ lib/plugins/stonith/external/kdumpcheck \ lib/plugins/stonith/external/ssh \ lib/plugins/stonith/external/ippower9258 \ lib/plugins/stonith/external/xen0-ha \ lrm/Makefile \ lrm/lrmd/Makefile \ lrm/admin/Makefile \ lrm/admin/cibsecret \ lrm/test/Makefile \ lrm/test/regression.sh \ lrm/test/lrmregtest \ lrm/test/LRMBasicSanityCheck \ lrm/test/testcases/Makefile \ logd/Makefile \ logd/logd \ replace/Makefile \ hb_report/Makefile \ hb_report/hb_report \ doc/Makefile \ doc/ha_logd.xml \ doc/ha_logger.xml \ doc/stonith.xml \ doc/meatclient.xml \ doc/stonith/Makefile ) dnl Now process the entire list of files added by previous dnl calls to AC_CONFIG_FILES() AC_OUTPUT() dnl ***************** dnl Configure summary dnl ***************** AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION} (Build: $GLUE_BUILD_VERSION)]) AC_MSG_RESULT([ Features =${FEATURES}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ Documentation = ${docdir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ Use system LTDL = ${ac_cv_lib_ltdl_lt_dlopen}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ HA group name = ${GLUE_DAEMON_GROUP}]) AC_MSG_RESULT([ HA user name = ${GLUE_DAEMON_USER}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Libraries = ${LIBS}]) AC_MSG_RESULT([ Stack Libraries = ${CLUSTERLIBS}]) Reusable-Cluster-Components-glue--3cff550e1084/doc/Makefile.am0000644000000000000000000000321212120057602024060 0ustar00usergroup00000000000000# # heartbeat: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in hb_report.xml ha_logd.xml ha_logger.xml stonith.xml meatclient.xml CLEANFILES = $(man_MANS) SUBDIRS = stonith hanoarchdir = $(datadir)/heartbeat man_MANS = if BUILD_DOC man_MANS += hb_report.8 ha_logd.8 ha_logger.1 stonith.8 meatclient.8 EXTRA_DIST = $(man_MANS) STYLESHEET_PREFIX ?= http://docbook.sourceforge.net/release/xsl/current MANPAGES_STYLESHEET ?= $(STYLESHEET_PREFIX)/manpages/docbook.xsl HTML_STYLESHEET ?= $(STYLESHEET_PREFIX)/xhtml/docbook.xsl FO_STYLESHEET ?= $(STYLESHEET_PREFIX)/fo/docbook.xsl XSLTPROC_OPTIONS ?= --xinclude XSLTPROC_MANPAGES_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_HTML_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_FO_OPTIONS ?= $(XSLTPROC_OPTIONS) %.5 %.8 %.1: %.xml $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) $< hb_report.8: hb_report.8.txt a2x -f manpage $< endif Reusable-Cluster-Components-glue--3cff550e1084/doc/ha_logd.xml.in0000644000000000000000000001006012120057602024547 0ustar00usergroup00000000000000 December 8, 2009 @PACKAGE_NAME@ @VERSION@ Alan Robertson ha_logd alanr@unix.sh Shi Guochun ha_logd gshi@ncsa.uiuc.edu Lars Marowsky-Bree ha_logd lmb@suse.de Florian Haas man page florian.haas@linbit.com ha_logd 8 System administration utilities ha_logd Logging Daemon for High-Availability Linux ha_logd file Description ha_logd is a logging daemon for Linux-HA. It receives messages from a local domain socket @HA_LOGDAEMON_IPC@, and writes them to appropriate files and syslog if enabled. The reason for utilizing this logging daemon is that occasionally Heartbeat suffers from disk I/O delays. By sending log messages to a logging daemon, heartbeat can avoid such I/O delays. Options The following options are supported: Show ha_logd status (either running or stopped) Stop (kill) the daemon Daemonize (without this option, ha_logd will run in the foreground) Show a brief usage message file Configuration file. You may configure a regular log file, debug log file, log facility, and entity. For details, see the example ha_logd.cf file found in the documentation. Files @GLUE_STATE_DIR@/ha_logd.pid – PID file ha_logd.cf – example configuration file See also heartbeat8, ha_logger1 Reusable-Cluster-Components-glue--3cff550e1084/doc/ha_logger.xml.in0000644000000000000000000000621712120057602025112 0ustar00usergroup00000000000000 December 8, 2009 @PACKAGE_NAME@ @VERSION@ Alan Robertson ha_logd alanr@unix.sh Shi Guochun ha_logd gshi@ncsa.uiuc.edu Lars Marowsky-Bree ha_logd lmb@suse.de Florian Haas man page florian.haas@linbit.com ha_logger 1 User commands ha_logger Log a message to files and/or syslog through the HA Logging Daemon ha_logger ha-log ha-debug tag message Description ha_logger is used to log a message to files/syslog through the HA Logging Daemon. Options The following options are supported: ha-log|ha-debug Log the message to different files. ha-log will log the message to the log file and the debug file, while ha-debug will log the message to the debug file only. tag Mark every line in the log with the specified tag. message The message that should be logged. See also heartbeat8, ha_logd8 Reusable-Cluster-Components-glue--3cff550e1084/doc/hb_report.8.txt0000644000000000000000000003647612120057602024741 0ustar00usergroup00000000000000:man source: hb_report :man version: 1.2 :man manual: Pacemaker documentation hb_report(8) ============ NAME ---- hb_report - create report for CRM based clusters (Pacemaker) SYNOPSIS -------- *hb_report* -f {time|"cts:"testnum} [-t time] [-u user] [-l file] [-n nodes] [-E files] [-p patt] [-L patt] [-e prog] [-MSDCZAVsvhd] [dest] DESCRIPTION ----------- The hb_report(1) is a utility to collect all information (logs, configuration files, system information, etc) relevant to Pacemaker (CRM) over the given period of time. OPTIONS ------- dest:: The report name. It can also contain a path where to put the report tarball. If left out, the tarball is created in the current directory named "hb_report-current_date", for instance hb_report-Wed-03-Mar-2010. *-d*:: Don't create the compressed tar, but leave the result in a directory. *-f* { time | "cts:"testnum }:: The start time from which to collect logs. The time is in the format as used by the Date::Parse perl module. For cts tests, specify the "cts:" string followed by the test number. This option is required. *-t* time:: The end time to which to collect logs. Defaults to now. *-n* nodes:: A list of space separated hostnames (cluster members). hb_report may try to find out the set of nodes by itself, but if it runs on the loghost which, as it is usually the case, does not belong to the cluster, that may be difficult. Also, OpenAIS doesn't contain a list of nodes and if Pacemaker is not running, there is no way to find it out automatically. This option is cumulative (i.e. use -n "a b" or -n a -n b). *-l* file:: Log file location. If, for whatever reason, hb_report cannot find the log files, you can specify its absolute path. *-E* files:: Extra log files to collect. This option is cumulative. By default, /var/log/messages are collected along with the cluster logs. *-M*:: Don't collect extra log files, but only the file containing messages from the cluster subsystems. *-L* patt:: A list of regular expressions to match in log files for analysis. This option is additive (default: "CRIT: ERROR:"). *-p* patt:: Additional patterns to match parameter name which contain sensitive information. This option is additive (default: "passw.*"). *-A*:: This is an OpenAIS cluster. hb_report has some heuristics to find the cluster stack, but that is not always reliable. By default, hb_report assumes that it is run on a Heartbeat cluster. *-u* user:: The ssh user. hb_report will try to login to other nodes without specifying a user, then as "root", and finally as "hacluster". If you have another user for administration over ssh, please use this option. *-S*:: Single node operation. Run hb_report only on this node and don't try to start slave collectors on other members of the cluster. Under normal circumstances this option is not needed. Use if ssh(1) does not work to other nodes. *-Z*:: If the destination directory exist, remove it instead of exiting (this is default for CTS). *-V*:: Print the version including the last repository changeset. *-v*:: Increase verbosity. Normally used to debug unexpected behaviour. *-h*:: Show usage and some examples. *-D* (obsolete):: Don't invoke editor to fill the description text file. *-e* prog (obsolete):: Your favourite text editor. Defaults to $EDITOR, vim, vi, emacs, or nano, whichever is found first. *-C* (obsolete):: Remove the destination directory once the report has been put in a tarball. EXAMPLES -------- Last night during the backup there were several warnings encountered (logserver is the log host): logserver# hb_report -f 3:00 -t 4:00 -n "node1 node2" report collects everything from all nodes from 3am to 4am last night. The files are compressed to a tarball report.tar.bz2. Just found a problem during testing: # note the current time node1# date Fri Sep 11 18:51:40 CEST 2009 node1# /etc/init.d/heartbeat start node1# nasty-command-that-breaks-things node1# sleep 120 #wait for the cluster to settle node1# hb_report -f 18:51 hb1 # if hb_report can't figure out that this is corosync node1# hb_report -f 18:51 -A hb1 # if hb_report can't figure out the cluster members node1# hb_report -f 18:51 -n "node1 node2" hb1 The files are compressed to a tarball hb1.tar.bz2. INTERPRETING RESULTS -------------------- The compressed tar archive is the final product of hb_report. This is one example of its content, for a CTS test case on a three node OpenAIS cluster: $ ls -RF 001-Restart 001-Restart: analysis.txt events.txt logd.cf s390vm13/ s390vm16/ description.txt ha-log.txt openais.conf s390vm14/ 001-Restart/s390vm13: STOPPED crm_verify.txt hb_uuid.txt openais.conf@ sysinfo.txt cib.txt dlm_dump.txt logd.cf@ pengine/ sysstats.txt cib.xml events.txt messages permissions.txt 001-Restart/s390vm13/pengine: pe-input-738.bz2 pe-input-740.bz2 pe-warn-450.bz2 pe-input-739.bz2 pe-warn-449.bz2 pe-warn-451.bz2 001-Restart/s390vm14: STOPPED crm_verify.txt hb_uuid.txt openais.conf@ sysstats.txt cib.txt dlm_dump.txt logd.cf@ permissions.txt cib.xml events.txt messages sysinfo.txt 001-Restart/s390vm16: STOPPED crm_verify.txt hb_uuid.txt messages sysinfo.txt cib.txt dlm_dump.txt hostcache openais.conf@ sysstats.txt cib.xml events.txt logd.cf@ permissions.txt The top directory contains information which pertains to the cluster or event as a whole. Files with exactly the same content on all nodes will also be at the top, with per-node links created (as it is in this example the case with openais.conf and logd.cf). The cluster log files are named ha-log.txt regardless of the actual log file name on the system. If it is found on the loghost, then it is placed in the top directory. If not, the top directory ha-log.txt contains all nodes logs merged and sorted by time. Files named messages are excerpts of /var/log/messages from nodes. Most files are copied verbatim or they contain output of a command. For instance, cib.xml is a copy of the CIB found in /var/lib/heartbeat/crm/cib.xml. crm_verify.txt is output of the crm_verify(8) program. Some files are result of a more involved processing: *analysis.txt*:: A set of log messages matching user defined patterns (may be provided with the -L option). *events.txt*:: A set of log messages matching event patterns. It should provide information about major cluster motions without unnecessary details. These patterns are devised by the cluster experts. Currently, the patterns cover membership and quorum changes, resource starts and stops, fencing (stonith) actions, and cluster starts and stops. events.txt is always generated for each node. In case the central cluster log was found, also combined for all nodes. *permissions.txt*:: One of the more common problem causes are file and directory permissions. hb_report looks for a set of predefined directories and checks their permissions. Any issues are reported here. *backtraces.txt*:: gdb generated backtrace information for cores dumped within the specified period. *sysinfo.txt*:: Various release information about the platform, kernel, operating system, packages, and anything else deemed to be relevant. The static part of the system. *sysstats.txt*:: Output of various system commands such as ps(1), uptime(1), netstat(8), and ifconfig(8). The dynamic part of the system. description.txt should contain a user supplied description of the problem, but since it is very seldom used, it will be dropped from the future releases. PREREQUISITES ------------- ssh:: It is not strictly required, but you won't regret having a password-less ssh. It is not too difficult to setup and will save you a lot of time. If you can't have it, for example because your security policy does not allow such a thing, or you just prefer menial work, then you will have to resort to the semi-manual semi-automated report generation. See below for instructions. + If you need to supply a password for your passphrase/login, then always use the `-u` option. sudo:: If the ssh user (as specified with the `-u` option) is other than `root`, then `hb_report` uses `sudo` to collect the information which is readable only by the `root` user. In that case it is required to setup the `sudoers` file properly. The user (or group to which the user belongs) should have the following line: + ALL = NOPASSWD: /usr/sbin/hb_report + See the `sudoers(5)` man page for more details. Times:: In order to find files and messages in the given period and to parse the `-f` and `-t` options, `hb_report` uses perl and one of the `Date::Parse` or `Date::Manip` perl modules. Note that you need only one of these. Furthermore, on nodes which have no logs and where you don't run `hb_report` directly, no date parsing is necessary. In other words, if you run this on a loghost then you don't need these perl modules on the cluster nodes. + On rpm based distributions, you can find `Date::Parse` in `perl-TimeDate` and on Debian and its derivatives in `libtimedate-perl`. Core dumps:: To backtrace core dumps gdb is needed and the packages with the debugging info. The debug info packages may be installed at the time the report is created. Let's hope that you will need this really seldom. TIMES ----- Specifying times can at times be a nuisance. That is why we have chosen to use one of the perl modules--they do allow certain freedom when talking dates. You can either read the instructions at the http://search.cpan.org/dist/TimeDate/lib/Date/Parse.pm#EXAMPLE_DATES[Date::Parse examples page]. or just rely on common sense and try stuff like: 3:00 (today at 3am) 15:00 (today at 3pm) 2007/9/1 2pm (September 1st at 2pm) Tue Sep 15 20:46:27 CEST 2009 (September 15th etc) `hb_report` will (probably) complain if it can't figure out what do you mean. Try to delimit the event as close as possible in order to reduce the size of the report, but still leaving a minute or two around for good measure. `-f` is not optional. And don't forget to quote dates when they contain spaces. Should I send all this to the rest of Internet? ----------------------------------------------- By default, the sensitive data in CIB and PE files is not mangled by `hb_report` because that makes PE input files mostly useless. If you still have no other option but to send the report to a public mailing list and do not want the sensitive data to be included, use the `-s` option. Without this option, `hb_report` will issue a warning if it finds information which should not be exposed. By default, parameters matching 'passw.*' are considered sensitive. Use the `-p` option to specify additional regular expressions to match variable names which may contain information you don't want to leak. For example: # hb_report -f 18:00 -p "user.*" -p "secret.*" /var/tmp/report Heartbeat's ha.cf is always sanitized. Logs and other files are not filtered. LOGS ---- It may be tricky to find syslog logs. The scheme used is to log a unique message on all nodes and then look it up in the usual syslog locations. This procedure is not foolproof, in particular if the syslog files are in a non-standard directory. We look in /var/log /var/logs /var/syslog /var/adm /var/log/ha /var/log/cluster. In case we can't find the logs, please supply their location: # hb_report -f 5pm -l /var/log/cluster1/ha-log -S /tmp/report_node1 If you have different log locations on different nodes, well, perhaps you'd like to make them the same and make life easier for everybody. Files starting with "ha-" are preferred. In case syslog sends messages to more than one file, if one of them is named ha-log or ha-debug those will be favoured over syslog or messages. hb_report supports also archived logs in case the period specified extends that far in the past. The archives must reside in the same directory as the current log and their names must be prefixed with the name of the current log (syslog-1.gz or messages-20090105.bz2). If there is no separate log for the cluster, possibly unrelated messages from other programs are included. We don't filter logs, but just pick a segment for the period you specified. MANUAL REPORT COLLECTION ------------------------ So, your ssh doesn't work. In that case, you will have to run this procedure on all nodes. Use `-S` so that `hb_report` doesn't bother with ssh: # hb_report -f 5:20pm -t 5:30pm -S /tmp/report_node1 If you also have a log host which is not in the cluster, then you'll have to copy the log to one of the nodes and tell us where it is: # hb_report -f 5:20pm -t 5:30pm -l /var/tmp/ha-log -S /tmp/report_node1 OPERATION --------- hb_report collects files and other information in a fairly straightforward way. The most complex tasks are discovering the log file locations (if syslog is used which is the most common case) and coordinating the operation on multiple nodes. The instance of hb_report running on the host where it was invoked is the master instance. Instances running on other nodes are slave instances. The master instance communicates with slave instances by ssh. There are multiple ssh invocations per run, so it is essential that the ssh works without password, i.e. with the public key authentication and authorized_keys. The operation consists of three phases. Each phase must finish on all nodes before the next one can commence. The first phase consists of logging unique messages through syslog on all nodes. This is the shortest of all phases. The second phase is the most involved. During this phase all local information is collected, which includes: - logs (both current and archived if the start time is far in the past) - various configuration files (corosync, heartbeat, logd) - the CIB (both as xml and as represented by the crm shell) - pengine inputs (if this node was the DC at any point in time over the given period) - system information and status - package information and status - dlm lock information - backtraces (if there were core dumps) The third phase is collecting information from all nodes and analyzing it. The analyzis consists of the following tasks: - identify files equal on all nodes which may then be moved to the top directory - save log messages matching user defined patterns (defaults to ERRORs and CRITical conditions) - report if there were coredumps and by whom - report crm_verify(8) results - save log messages matching major events to events.txt - in case logging is configured without loghost, node logs and events files are combined using a perl utility BUGS ---- Finding logs may at times be extremely difficult, depending on how weird the syslog configuration. It would be nice to ask syslog-ng developers to provide a way to find out the log destination based on facility and priority. If you think you found a bug, please rerun with the -v option and attach the output to bugzilla. hb_report can function in a satisfactory way only if ssh works to all nodes using authorized_keys (without password). There are way too many options. AUTHOR ------ Written by Dejan Muhamedagic, RESOURCES --------- Pacemaker: Heartbeat and other Linux HA resources: OpenAIS: Corosync: SEE ALSO -------- Date::Parse(3) COPYING ------- Copyright \(C) 2007-2009 Dejan Muhamedagic. Free use of this software is granted under the terms of the GNU General Public License (GPL). Reusable-Cluster-Components-glue--3cff550e1084/doc/meatclient.xml.in0000644000000000000000000000477012120057602025312 0ustar00usergroup00000000000000 December 4, 2009 Cluster Glue @VERSION@ Gregor Binder meatclient gbinder@sysfive.com Michael Mörz man page mimem@debian.org Simon Horman man page horms@vergenet.net Florian Haas man page florian.haas@linbit.com meatclient 8 System administration utilities meatclient Manually confirm that a node has been removed from the cluster meatclient nodename Description meatclient confirms that a node has been manually removed from the cluster. It instructs the cluster manager, via the meatware STONITH plugin, that it is safe to continue cluster operations. Options The following options are supported: nodename nodename is the name of the cluster node that has been fenced. See also heartbeat8, stonith8 Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith.xml.in0000644000000000000000000003160612120057602024653 0ustar00usergroup00000000000000 December 7, 2009 @PACKAGE_NAME@ @VERSION@ Alan Robertson stonith alanr@unix.sh Simon Horman man page horms@vergenet.net Florian Haas man page florian.haas@linbit.com stonith 8 System administration utilities stonith extensible interface for remotely powering down a node in the cluster stonith stonith stonith stonith-device-type stonith stonith-device-type name=value stonith-device-parameters stonith-device-parameters-file count stonith stonith-device-type name=value stonith-device-parameters stonith-device-parameters-file count reset on off nodename Description The STONITH module provides an extensible interface for remotely powering down a node in the cluster (STONITH = Shoot The Other Node In The Head). The idea is quite simple: when the software running on one machine wants to make sure another machine in the cluster is not using a resource, pull the plug on the other machine. It's simple and reliable, albeit admittedly brutal. Options The following options are supported: count Perform any actions identified by the , and options count times. stonith-device-parameters-file Path of file specifying parameters for a stonith device. To determine the syntax of the parameters file for a given device type run: # stonith -t stonith-device-type -n All of the listed parameters need to appear in order on a single line in the parameters file and be delimited by whitespace. Display detailed information about a stonith device including description, configuration information, parameters and any other related information. When specified without a stonith-device-type, detailed information on all stonith devices is displayed. If you don't yet own a stonith device and want to know more about the ones we support, this information is likely to be helpful. List the valid stonith device types, suitable for passing as an argument to the option. List the hosts controlled by the stonith device. Output the parameter names of the stonith device. name=value Parameter, in the form of a name/value pair, to pass directly to the stonith device. To determine the syntax of the parameters for a given device type run: # stonith -t stonith-device-type -n All of the listed parameter names need to be passed with their corresponding values. stonith-device-parameters Parameters to pass directly to the stonith device. To determine the syntax of the parameters for a given device type run: # stonith -t stonith-device-type -n All of the listed parameter names need to appear in order and be delimited by whitespace. Show the status of the stonith device. Silent operation. Suppress logging of error messages to standard error. action The stonith action to perform on the node identified by nodename. Chosen from reset, on, and off. If a nodename is specified without the option, the stonith action defaults to reset. stonith-device-type The type of the stonith device to be used to effect stonith. A list of supported devices for an installation may be obtained using the option. Ignored. Examples To determine which stonith devices are available on your installation, use the option: # stonith -L All of the supported devices will be displayed one per line. Choose one from this list that is best for your environment - let's use wti_nps for the rest of this example. To get detailed information about this device, use the option: # stonith -t wti_nps -h Included in the output is the list of valid parameter names for wti_nps. To get just the list of valid parameter names, use the option instead: # stonith -t wti_nps -n All of the required parameter names will be displayed one per line. For wti_nps the output is: ipaddr password There are three ways to pass these parameters to the device. The first (and preferred) way is by passing name/value pairs on the stonith command line: # stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw ... The second way, which is maintained only for backward compatibility with legacy clusters, is passing the values in order on the stonith command line with the option: # stonith -t wti_nps -p "my-dev-ip my-dev-pw" ... The third way, which is also maintained only for backward compatibility with legacy clusters, is placing the values in order on a single line in a config file: my-dev-ip my-dev-pw ... and passing the name of the file on the stonith command line with the option: # stonith -t wti_nps -F ~/my-wtinps-config ... To make sure you have the configuration set up correctly and that the device is available for stonith operations, use the option: # stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -S If all is well at this point, you should see something similar to: stonith: wti_nps device OK. If you don't, some debugging may be necessary to determine if the config info is correct, the device is powered on, etc. The option can come in handy here - you can add it to any stonith command to cause it to generate debug output. To get the list of hosts controlled by the device, use the option: # stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -l All of the hosts controlled by the device will be displayed one per line. For wti_nps the output could be: node1 node2 node3 To power off one of these hosts, use the option: # stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -T off node See also heartbeat8, meatclient8 Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/Makefile.am0000644000000000000000000000217312120057602025555 0ustar00usergroup00000000000000# This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in stdocdir = $(docdir)/stonith stdoc_DATA = README.bladehpi \ README.cyclades \ README.drac3 \ README.dracmc \ README.external \ README.ibmrsa \ README.ibmrsa-telnet \ README.ipmilan \ README.meatware \ README.rackpdu \ README.rcd_serial \ README.riloe \ README.vacm \ README.wti_mpc \ README_kdumpcheck.txt \ README.vcenter Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.bladehpi0000644000000000000000000000771012120057602026152 0ustar00usergroup00000000000000 STONITH module for IBM BladeCenter via OpenHPI ---------------------------------------------- Requirements: Linux-HA bladehpi STONITH plugin requires OpenHPI 2.6+ OpenHPI requires Net-SNMP 5.0+ OpenHPI requires BladeCenter Management Module 1.08+ This STONITH module talks to IBM BladeCenters via SNMP through use of the OpenHPI BladeCenter plugin (snmp_bc). For more information about installing OpenHPI, setting up the BladeCenter SNMP agent, etc. please visit http://www.openhpi.org/. Once OpenHPI is installed properly, the STONITH plugin will automatically be built the next time Linux-HA is built. Use the OpenHPI configuration file (i.e. /etc/openhpi/openhpi.conf) to configure the BladeCenters of interest to STONITH. For example, the following excerpt: plugin libsnmp_bc handler libsnmp_bc { entity_root = "{SYSTEM_CHASSIS,1}" # Required host = "9.254.253.252" # Required community = "community" # Version 1 Required. version = "3" # Required. SNMP protocol version (1|3) security_name = "userid" # Version 3 Required. passphrase = "userpass" # Version 3. Required if security_level is authNoPriv or authPriv. auth_type = "MD5" # Version 3. Passphrase encoding (MD5|SHA) security_level = "authNoPriv" # Version 3. (noAuthNoPriv|authNoPriv|authPriv) } defines how to access the BladeCenter at 9.254.253.252 using SNMPV3 with an ID/password of userid/userpass. The entity_root must be passed to the STONITH bladehpi plugin as its single required parameter. For example, to query the list of blades present in the BladeCenter configured above, run: stonith -t bladehpi -p "{SYSTEM_CHASSIS,1}" -l which is the same as: stonith -t bladehpi "entity_root={SYSTEM_CHASSIS,1}" -l Use the BladeCenter Management Module web interface to set the Blade Information to match "uname -n" for each blade in the cluster. For example, with the BladeCeter configured above use a brower to access http://9.254.253.252, login with userid/userpass, and then go to Blade Tasks -> Configuration -> Blade Information, enter the proper names, and select Save. Be aware that heartbeat must be restarted before these changes take effect or, if using the OpenHPI daemon, the daemon must be restarted. More than one BladeCenter can be placed in the OpenHPI configuration file by using different numbers with the entity_root. For example, plugin libsnmp_bc handler libsnmp_bc { entity_root = "{SYSTEM_CHASSIS,1}" # Required host = "9.254.253.252" # Required : } handler libsnmp_bc { entity_root = "{SYSTEM_CHASSIS,2}" # Required host = "9.254.253.251" # Required : } There is an optional parameter, soft_reset, that is true|1 if bladehpi should use soft reset (power cycle) to reset nodes or false|0 if it should use hard reset (power off, wait, power on); the default is false. As an example, to override the default value the above stonith command would become: stonith -t bladehpi -p "{SYSTEM_CHASSIS,1} true" -l which is the same as: stonith -t bladehpi "entity_root={SYSTEM_CHASSIS,1} soft_reset=true" -l The difference between the two is that a soft reset is much quicker but may return before the node has been reset because bladehpi relies on BladeCenter firmware to cycle the node's power, while a hard reset is slower but guaranteed not to return until the node is dead because bladehpi powers off the node, waits until it is off, then powers it on again. NOTE: Set the OPENHPI_CONF environment variable to contain the fully-qualified path of the OpenHPI configuration file, for example: export OPENHPI_CONF=/etc/openhpi/openhpi.conf NOTE: If OpenHPI is not configured with --disable-daemon before being built and installed, make sure that the OpenHPI daemon is running before using the bladehpi plugin. NOTE: If debugging of the environment is needed, configure OpenHPI with --enable-debuggable and rebuild/reinstall, export OPENHPI_DEBUG=YES, and run stonith commands with the -d option. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.cyclades0000644000000000000000000000411212120057602026162 0ustar00usergroup00000000000000STONITH module for Cyclades AlterPath PM ---------------------------------------- This STONITH module talks to Cyclades AlterPath PM series of power managers via TS, ACS or KVM equipment. Access to the frontend device (TS, ACS or KVM) is done via root user with passwordless ssh. For that, it is necessary to create a public/private keypar with _empty_ passphrase on _each_ machine which is part of the cluster. Small HOWTO follows: # ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/home/root/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: dc:e0:71:55:fd:2a:b0:19:d6:3c:48:e5:45:db:b4:be root@hostname.network Next step is to append the public key (/root/.ssh/id_rsa.pub) to the authorized_keys file on the TS/ACS/KVM box. The authorized keys file location is set at the SSH daemon configuration file. The default location is /etc/ssh/authorized_keys, so: [root@clusterhost]# scp /root/.ssh/id_rsa.pub root@alterpath:/tmp login to the TS/ACS/KVM box normally and append the public key. # ssh root@alterpath Password: .... [root@CAS root]# cat /tmp/id_rsa.pub >> /etc/ssh/authorized_keys The following entries must be present on /etc/ssh/sshd_config for the passwordless scheme to work properly: RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile /etc/ssh/authorized_keys Next step is to test if the configuration has been done successfully: [root@clusterhost root]# ssh root@alterpath [root@CAS root]# If it logins automatically without asking for a password, then everything has been done correctly! Note that such configuration procedure (including generation of the key pair) has to be done for each machine in the cluster which intends to use the AlterPath PM as a STONITH device. ------ Any questions please contact Cyclades support at or Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.drac30000644000000000000000000000123512120057602025372 0ustar00usergroup00000000000000Stonith module for Dell DRACIII remote access card -------------------------------------------------- This module uses the Dell DRACIII PCI card as a stonith device. It sends the XML commands over HTTPS to the DRACIII web server. The card firmware must be version 2.0 at least, with support for SSL based service and many bug fixes over 1.x versions. This module uses libcurl, libxml2 (gnome xml libs) and libssl. Any hints, bug reports, improvements, etc. will be apreciated. --- Roberto Moreda http://www.alfa21.com Alfa21 A Coruña (Spain) UNIX, Linux & TCP/IP Services - High Availability Solutions Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.dracmc0000644000000000000000000001053412120057602025631 0ustar00usergroup00000000000000dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki) Connects to Dell Drac/MC Blade Enclosure via a Cyclades terminal server with telnet and switches power of named blade servers appropriatelly. Description: Dell offers the Drac/MC in their blade enclosures. The Drac/MC can be accessed in different ways. One way to interface to it is to connect the blade enclosure's Drac/MC serial port to a Cyclades terminal server. You can then access the Drac/MC via telnet via the Cyclades. Once logged in, you can use 'help' to show all available commands. With the 'serveraction' command, you can control both hard and soft resets as well as power to a particular blade. The blades are named 'Server-X', where 'X' is a number which corresponds to the blade number in the enclosure. This plugin allows using the Drac/MC with stonith. It uses python's standards 'telnetlib' library to log in and issue commands. The code is very similar to the original ibmrsa-telnet plugin released by Andreas and was quite easy to modify for this application. One complication is that the Cyclades only allows one active connection. If someone or something has a connection active, the terminal server closes the new attempted connection. Since this situation can be common, for example if trying to stonith two blades or when the plugin is started by multiple cluster nodes, there is a built in retry mechanism for login. On 10 retries, the code gives up and throws. When running this resource, it is best to not run it as a clone, rather as a normal, single-instance resource. Make sure to create a location constraint that excludes the node that is to be fenced. Required parameters: nodename: The name of the server you want to touch on your network cyclades_ip: The IP address of the cyclades terminal server cyclades_port: The port for telnet to access on the cyclades (i.e. 7032) servername: The DRAC/MC server name of the blade (i.e. Server-7) username: The login user name for the DRAC/MC password: The login password for the DRAC/MC Example configuration These are examples: you should adjust parameters, scores and timeout values to fit your environment. crm shell: primitive fence_node1 stonith:external/dracmc-telnet \ nodename=node1 cyclades_ip=10.0.0.1 cyclades_port=7001 \ servername=Server-1 username=USERID password=PASSWORD \ op monitor interval="200m" timeout="60s" location loc-fence_node1 fence_node1 -inf: node1 XML: Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.external0000644000000000000000000000662112120057602026224 0ustar00usergroup00000000000000EXTERNAL module for Linux-HA STONITH This stonith plugin runs an external command written in your favorite language to shutdown the given host. The external command should return a zero exit status after a successful shutdown, or non-zero exit status for a shutdown failure. Failures notifications will be sent to syslog. To create your own external plugin, write a script that supports the following actions: reset on (optional) off (optional) gethosts status getconfignames getinfo-devid getinfo-devname getinfo-devdescr getinfo-devurl getinfo-xml and place it in the /usr/lib/stonith/plugins/external directory - the script must be a regular executable file that is NOT writable by group or others in order to be recognized as an external plugin. If the action requires information to be returned, such as the list of hosts or config names or any of the getinfo calls, simply write the information to stdout. When complete, return zero to indicate the action succeeded or non-zero to indicate the action failed. You can use the ssh (sh) and riloe (pyhton) scripts already in that directory as working examples. To make sure that your external plugin is recognized, run "stonith -L" and look for its name in the output, something along the lines of: external/yourplugin To configure the plugin on an R1 (legacy) cluster, add a line similar to the following to /etc/ha.d/ha.cf: stonith external/yourplugin /etc/ha.d/yourplugin.cfg where /etc/ha.d/yourplugin.cfg contains a single line with all of your plugin's parameters: parm1-value parm2-value ... Another way to configure the plugin on a legacy cluster is to add a line similiar to the following to /etc/ha.d/ha.cf instead: stonith_host * external/yourplugin parm1-value parm2-value ... where all of your plugin's parameters are placed at the end of the line. Please note that all parameters come in to the plugin in name/value (environment variable) form, but in R1 configurations, they appear as a list of parameters. They are ordered in the config file or on the stonith_host line according to the ordering specified in the output of the getconfignames operation. To configure the plugin on an R2 cluster, place lines similar to the following into the section of your CIB, which is contained in /var/lib/heartbeat/crm/cib.xml: Whatever parameters specified in the section of the CIB are passed to the script as environment variables. For the example above, the parameters are passed as parm1-name=parm1-value, parm2-name=parm2-value and so on. Additional information can be found at http://linux-ha.org/wiki/ExternalStonithPlugins. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.ibmrsa0000644000000000000000000000042012120057602025646 0ustar00usergroup00000000000000See ftp://ftp.software.ibm.com/systems/support/system_x_pdf/d3basmst.pdf ftp://ftp.software.ibm.com/systems/support/system_x_pdf/88p9248.pdf http://www.redbooks.ibm.com/abstracts/sg246495.html for documentation about IBM management processors and the IBMmpcli utility. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.ibmrsa-telnet0000644000000000000000000000505512120057602027150 0ustar00usergroup00000000000000ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki) Connects to IBM RSA Board via telnet and switches power of server appropriately. Description: IBM offers Remote Supervisor Adapters II for several servers. These RSA boards can be accessed in different ways. One of that is via telnet. Once logged in you can use 'help' to show all available commands. With 'power' you can reset, power on and off the controlled server. This command is used in combination with python's standard library 'telnetlib' to do it automatically. Code snippet for cib It's useful to give a location preference so that the stonith agent is run on the/an other node. This is not necessary as one node can kill itself via RSA Board. But: If this node becomes crazy my experiences showed that the node is not able to shoot itself anymore properly. You have to adjust parameters, scores and timeout values to fit your HA environment. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.ipmilan0000644000000000000000000001100512120057602026023 0ustar00usergroup00000000000000 IPMILAN STONITH Module Copyright (c) 2003 Intel Corp. yixiong.zou@intel.com 1. Intro IPMILAN STONITH module works by sending a node an IPMI message, in particular, a 'chassis control' command. Currently the message is sent over the LAN. 2. Hardware Requirement In order to use this module, the node has to be IPMI v1.5 compliant and also supports IPMI over LAN. For example, the Intel Langley platform. Note: IPMI over LAN is an optional feature defined by IPMI v1.5 spec. So even if a system is IPMI compliant/compatible, it might still not support IPMI over LAN. If you are sure this is your case and you still want to try this plugin, read section 6, IPMI v1.5 without IPMI over LAN Support. 3. Software Requirement This module needs OpenIPMI (http://openipmi.sf.net) to compile. Version 1.4.x or 2.0.x is supported. 4. Hardware Configuration How to configure the node so it accepts IPMI lan packets is beyond the scope of this document. Consult your product manual for this. 5. STONITH Configuration Each node in the cluster has to be configured individually. So normally there would be at least two entries, unless you want to use a different STONITH device for the other nodes in the cluster. ;) The configuration file syntax looks like this: ... node: the hostname. ip: the IP address of the node. If a node has more than one IP addresses, this is the IP address of the interface which accepts IPMI messages. :) port: the port number to send the IPMI message to. The default is 623. But it could be different or even configurable. auth: the authorization type of the IPMI session. Valid choices are "none", "straight", "md2", and "md5". priv: the privilege level of the user. Valid choices are "operator" or "admin". These are the privilege levels required to run the 'chassis control' command. user: the username. use "" if it is empty. Cannot exceed 16 characters. pass: the password. use "" if it is empty. Cannot exceed 16 characters. reset_method: (optional) which IPMI chassis control to send to reset the host. Possible values are power_cycle (default) and hard_reset. Each line is white-space delimited and lines begins with '#' are ignored. 6. IPMI v1.5 without IPMI over LAN Support If somehow your computer have a BMC but without LAN support, you might still be able to use this module. 0) Make sure OpenIPMI is installed. OpenIPMI 1.0.3 should work. 1) Create a /etc/ipmi_lan.conf file. Here's a sample of how this file should look like addr 172.16.1.249 999 PEF_alerting on per_msg_auth off priv_limit admin allowed_auths_admin none md2 md5 user 20 on "" "" admin 5 md2 md5 none If you do not understand what each line means, do a man on ipmilan. 2) run ipmilan as root. 3) Try send youself an IPMI packet over the network using ipmicmd see if it works. ipmicmd -k "0f 00 06 01" lan 172.16.1.249 999 none admin "" "" The result should be something like: Connection 0 to the BMC is up0f 07 00 01 00 01 80 01 19 01 8f 77 00 00 4b 02 4) Configure your system so everytime it boots up, the ipmi device drivers are all loaded and ipmilan is run. This is all OS dependent so I can't tell you what to do. The major draw back of this is that you will not be able to power it up once it's power down, which for a real IPMI, you could. 7. Bugs Some IPMI device does not return 0x0, success, to the host who issued the reset command. A timeout, 0xc3, could be returned instead. So I am counting that also as a "successful reset". Note: This behavior is not fully IPMI v1.5 compliant. Based on the IPMI v1.5 spec, the IPMI device should return the appropriate return code. And it is even allowed to return the appropriate return code before performing the action. 8. TODO 1) Right now the timeout on each host is hard coded to be 10 seconds. It will be nice to be able to set this value for individual host. 2) A better way of detecting the success of the reset operation will be good. A lot of times the host which carried out the reset does not return a success. 3) The os_handler should be contributed back to the OpenIPMI project so that we do not need to maintain it here. It does not make sense for every little app like this to write its own os_handler. A generic one like in this program should be sufficient. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.ippower92580000644000000000000000000000462612120057602026422 0ustar00usergroup00000000000000IP Power 9258 as external stonith device. ========================================= Device Information ================== Warning: ======== Aviosys provides different types and versions of IP Power 9258. The device is currently available with four or eight power outlets. This script was tested with firmware version: V1.55 2009/12/22 Especially "IP Power 9258 HP" uses a different http command interface. ====================================================================== Resources for device documentation: Manufacturer URL: http://www.aviosys.com/ippower9258.htm Manual URL: http://www.aviosys.com/manual.htm Manual current version URL: http://www.aviosys.com/images/9258_manual_20081104.pdf The documentation of the http command interface defines three supported commands: GetPower - useful for testing status of the device and of each port SetPower - used to control status of each power outlet SetSchedule+Power - useless for stonith Common documented structure of these three commands is http://username:password@a.b.c.d/Set.cmd?CMD=command[+param=value...] param is one or more of P60 to P67 and value is 0 or 1 expected response for GetPower is of the format P60=1,P61=0,P62=1,P63=1,P64=0,P65=0,P66=0,P67=0 SetPower does respond with the same format but restricts the list to the modified ports. P60 to P67 represent the status of the power outlet 1 to 8: 0 <=> power off; 1 <=> power on. IP Power 9258 allows to assign port names (pw1Name to pw8Name) to each port. These names can be used with the web interface (web form with post-method). Script specific notes ===================== There is no documented http command to retrieve port names via the http command interface. We try to get the hostlist via the web interface. This script assumes a one to one mapping between names of hostlist and port attributes of power outlet: 1st hostname in hostlist connected to 1st power outlet with port status P60 and port name pw1Name. ... 8th hostname in hostlist connected to 8th power outlet with port status P67 and port name pw8Name. If the hostlist parameter is not defined, then all assigned outlets are inserted into the hostlist. Unused outlets should have empty names. The node names obviously have to match the corresponding outlet names. A reserved hostname is "*not-defined*". This is a sript-internal placeholder for unused outlets. It does not appear in the hostlist. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.meatware0000644000000000000000000000122412120057602026201 0ustar00usergroup00000000000000 MEATWARE Module for Linux-HA STONITH ABOUT: This is a port of the "meatware" stomith method found in the GFS distribution (see http://globalfilesystem.org/) to the Linux-HA project. It notifies operators if a node needs to be reset and waits for confirmation. USAGE: The module can be used like any other stonith module. It will syslog a message at CRIT level if it needs an operator to power-cycle a node on its behalf. To confirm that a manual reset has been done, execute "meatclient -c ". If you abort the confirmation, the module will report that the reset has failed. AUTHOR: Gregor Binder Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.rackpdu0000644000000000000000000000121712120057602026027 0ustar00usergroup00000000000000APC Rack PDU The product information pages: http://www.apcc.com/products/family/index.cfm?id=70 The User's Guide: http://www.apcmedia.com/salestools/ASTE-6Z6KAV_R1_EN.pdf Apparently, an existing http or telnet session will make the plugin fail. In case your nodes are equipped with multiple power supplies, the PDU supports synchronous operation on multiple outlets on up to four Switched Rack PDUs. See the User's Guide for more information on how to setup outlet groups. NB: There has been a report by one user that in case a link between two PDUs in the chain is broken, the PDU returns success even though it failed. This needs to be verified. Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.rcd_serial0000644000000000000000000002174612120057602026516 0ustar00usergroup00000000000000rcd_serial - RC Delayed Serial ------------------------------ This stonith plugin uses one (or both) of the control lines of a serial device (on the stonith host) to reboot another host (the stonith'ed host) by closing its reset switch. A simple idea with one major problem - any glitch which occurs on the serial line of the stonith host can potentially cause a reset of the stonith'ed host. Such "glitches" can occur when the stonith host is powered up or reset, during BIOS detection of the serial ports, when the kernel loads up the serial port driver, etc. To fix this, you need to introduce a delay between the assertion of the control signal on the serial port and the closing of the reset switch. Then any glitches will be dissipated. When you really want to do the business, you hold the control signal high for a "long time" rather than just tickling it "glitch-fashion" by, e.g., using the rcd_serial plugin. As the name of the plugin suggests, one way to achieve the required delay is to use a simple RC circuit and an npn transistor: . . RTS . . ----------- +5V or ---------- . | DTR . | . Rl reset . | T1 . | |\logic . Rt | ------RWL--------| -------> . | b| /c . |/ . |---Rb---|/ . . | |\ . (m/b wiring typical . C | \e . only - YMMV!) . | | . . | | . SG ---------------------------RWG----------- 0V . . . . stonith'ed host stonith host --->.<----- RC circuit ----->.<---- RWL = reset wire live (serial port) . . RWG = reset wire ground The characteristic delay (in seconds) is given by the product of Rt (in ohms) and C (in Farads). Suitable values for the 4 components of the RC circuit above are: Rt = 20k C = 47uF Rb = 360k T1 = BC108 which gives a delay of 20 x 10e3 x 47 x 10e-6 = 0.94s. In practice the actual delay achieved will depend on the pull-up load resistor Rl if Rl is small: for Rl greater than 3k there is no significant dependence but lower than this and the delay will increase - to about 1.4s at 1k and 1.9s at 0.5k. This circuit will work but it is a bit dangerous for the following reasons: 1) If by mistake you open the serial port with minicom (or virtually any other piece of software) you will cause a stonith reset ;-(. This is because opening the port will by default cause the assertion of both DTR and RTS, and a program like minicom will hold them high thenceforth (unless and until a receive buffer overflow pulls RTS down). 2) Some motherboards have the property that when held in the reset state, all serial outputs are driven high. Thus, if you have the circuit above attached to a serial port on such a motherboard, if you were to press the (manual) reset switch and hold it in for more than a second or so, you will cause a stonith reset of the attached system ;-(. This problem can be solved by adding a second npn transistor to act as a shorting switch across the capacitor, driven by the other serial output: . . . . ----------- +5V RTS ----------------- . | . | . Rl reset . | T1 . | |\logic . Rt | ------RWL--------| -------> . | b| /c . |/ . T2 --|---Rb---|/ . . | / | |\ . (m/b wiring typical . b| /c | | \e . only - YMMV!) DTR ------Rb--|/ C | . . |\ | | . . | \e | | . . | | | . SG ----------------------------------RWG------------- 0V . . . . stonith'ed host stonith->.<--------- RC circuit ------->.<---- RWL = reset wire live host . . RWG = reset wire ground Now when RTS goes high it can only charge up C and cause a reset if DTR is simultaneously kept low - if DTR goes high, T2 will switch on and discharge the capacitor. Only a very unusual piece of software e.g. the rcd_serial plugin, is going to achieve this (rather bizarre) combination of signals (the "meaning" of which is something along the lines of "you are clear to send but I'm not ready"!). T2 can be another BC108 and with Rb the same. RS232 signal levels are typically +-8V to +-12V so a 16V rating or greater for the capacitor is sufficient BUT NOTE that a _polarised_ electrolytic should not be used because the voltage switches around as the capacitor charges. Nitai make a range of non-polar aluminium electrolytic capacitors. A 16V 47uF radial capacitor measures 6mm diameter by 11mm long and along with the 3 resistors (1/8W are fine) and the transistors, the whole circuit can be built in the back of a DB9 serial "plug" so that all that emerges from the plug are the 2 reset wires to go to the stonith'ed host's m/b reset pins. NOTE that with these circuits the reset wires are now POLARISED and hence they are labelled RWG and RWL above. You cannot connect to the reset pins either way round as you can when connecting a manual reset switch! You'll soon enough know if you've got it the wrong way round because your machine will be in permanent reset state ;-( How to find out if your motherboard can be reset by these circuits ------------------------------------------------------------------ You can either build it first and then suck it and see, or, you need a multimeter. The 0V rail of your system is available in either of the 2 black wires in the middle of a spare power connector (one of those horrible 4-way plugs which you push with difficulty into the back of hard disks, etc. Curse IBM for ever specifying such a monstrosity!). Likewise, the +5V rail is the red wire. (The yellow one is +12V, ignore this.) First, with the system powered down and the meter set to read ohms: check that one of the reset pins is connected to 0V - this then is the RWG pin; check that the other pin (RWL) has a high resistance wrt 0V (probably > 2M) and has a small resistance wrt to +5V - between 0.5k and 10k (or higher, doesn't really matter) will be fine. Second, with the system powered up and the meter set to read Volts: check that RWG is indeed that i.e. there should be 0V between it and the 0V rail; check that RWL is around +5V wrt the 0V rail. If all this checks out, you are _probably_ OK. However, I've got one system which checks out fine but actually won't work. The reason is that when you short the reset pins, the actual current drain is much higher than one would expect. Why, I don't know, but there is a final test you can do to detect this kind of system. With the system powered up and the meter set to read milliamps: short the reset pins with the meter i.e. reset the system, and note how much current is actually drained when the system is in the reset state. Mostly you will find that the reset current is 1mA or less and this is fine. On the system I mention above, it is 80mA! If the current is greater than 20mA or so, you have probably had it with the simple circuits above, although reducing the base bias resistor will get you a bit further. Otherwise, you have to use an analog switch (like the 4066 - I had to use 4 of these in parallel to reset my 80mA system) which is tedious because then you need a +5V supply rail to the circuit so you can no longer just build it in the back of a serial plug. Mail me if you want the details. With the circuit built and the rcd_serial plugin compiled, you can use: stonith -t rcd_serial -p "testhost /dev/ttyS0 rts XXX" testhost to test it. XXX is the duration in millisecs so just keep increasing this until you get a reset - but wait a few secs between each attempt because the capacitor takes time to discharge. Once you've found the minimum value required to cause a reset, add say 200ms for safety and use this value henceforth. Finally, of course, all the usual disclaimers apply. If you follow my advice and destroy your system, sorry. But it's highly unlikely: serial port outputs are internally protected against short circuits, and reset pins are designed to be short circuited! The only circumstance in which I can see a possibility of damaging something by incorrect wiring would be if the 2 systems concerned were not at the same earth potential. Provided both systems are plugged into the same mains system (i.e. are not miles apart and connected only by a very long reset wire ;-) this shouldn't arise. John Sutton john@scl.co.uk October 2002 Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.riloe0000644000000000000000000000207112120057602025507 0ustar00usergroup00000000000000Note for iLO 3 users This plugin doesn't support the iLO version 3. Please use ipmilan or external/ipmi, iLO3 should support IPMI. Alain St-Denis wrote the riloe plugin. Here is short usage: primitive st0 stonith:external/riloe \ hostlist=target-node \ ilo_hostname=ilo-ip-address \ ilo_user=admin ilo_password=secret ilo_protocol=2.0 The following additional parameters are available: ilo_can_reset: Set to "1" if the ilo is capable of rebooting the host. Defaults to '0'. ilo_protocol: Defaults to 1.2. Set to the protocol version ilo supports. ilo_powerdown_method: "button" or "power", the former simulates pressing the button, the latter pulling the power plug. Defaults to "power". The "button" method is easier on the host, but requires ACPI. "power" should be more reliable, but not to be used excessively for testing. ilo_proxyhost (string): Proxy hostname proxy hostname if required to access ILO board ilo_proxyport (string, [3128]): Proxy port proxy port if required to access ILO board parameter will be ignored if proxy hostname is not set Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.vacm0000644000000000000000000000251712120057602025330 0ustar00usergroup0000000000000020 December 2000 I (rather poorly) integrated this contributed stonith driver into the linux-ha-stonith release. There is a problem that needs to be resolved by autoconf in that the driver will not compile unless libvacmclient is installed on the system. For now, what I've done is included a line in stonith/Makefile that you can uncomment if you want to compile the vacm stonith module. Look in the Makefile in this directory for the following lines and do like it says # If you want the VA Linux Cluster stonith module installed, # uncomment the following line. You must have the vacmclient library #VACM_STONITH = vacm_stonith.so Please direct questions about the operation of the stonith module to Mike Tilstra (see the announcement to the linux-ha-dev mailing list attached below.) -Eric. eric.ayers@compgen.com ------------------------------------------------------------------------------ From: Mike Tilstra Sender: linux-ha-dev-admin@lists.tummy.com To: linux-ha-dev@lists.tummy.com Subject: [Linux-ha-dev] stonith module for VACM Date: Tue, 19 Dec 2000 16:41:38 -0600 This was in need for some testing I'm doing, so I hacked this up quick. It works for me, but I'm willing to bet there's atleast one bug in it. Figured others might like it. ... -- Mike Tilstra conrad@sistina.comReusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.vcenter0000644000000000000000000000601512120057602026045 0ustar00usergroup00000000000000VMware vCenter/ESX STONITH Module ================================= 1. Intro -------- VMware vCenter/ESX STONITH Module is intended to provide STONITH support to clusters in VMware Virtual Infrastructures. It is able to deal with virtual machines running on physically different HostSystems (e.g. ESX/ESXi) by using VMware vSphere Web Services SDK http://www.vmware.com/support/developer/vc-sdk/ and connecting directly on each HostSystem or through a VMware vCenter: in this last case the module locates the specified virtual machine in the Virtual Infrastructure and performs actions required by cluster policies. 2. Software requirements ------------------------ VMware vSphere CLI, which includes both CLI tools and Perl SDK http://www.vmware.com/support/developer/vcli/ . The plugin has been tested with version 4.1 http://www.vmware.com/download/download.do?downloadGroup=VCLI41 3. vCenter/ESX authentication settings -------------------------------------- Create the credentials file with credstore_admin.pl: /usr/lib/vmware-vcli/apps/general/credstore_admin.pl \ -s 10.1.1.1 -u myuser -p mypass This should create $HOME/.vmware/credstore/vicredentials.xml Copy it to a system folder, e.g. /etc cp -p $HOME/.vmware/credstore/vicredentials.xml /etc 4. Testing ---------- The plugin can be invoked directly to perform a very first connection test (replace all the provided sample values): VI_SERVER=10.1.1.1 \ VI_CREDSTORE=/etc/vicredentials.xml \ HOSTLIST="hostname1=vmname1;hostname2=vmname2" \ RESETPOWERON=0 \ /usr/lib/stonith/plugins/external/vcenter gethosts If everything works correctly you should get: hostname1 hostname2 When invoked in this way, the plugin connects to VI_SERVER, authenticates with credentials stored in VI_CREDSTORE and tries to retrieve the list of virtual machines (case insensitive) matching vmname1 and vmname2 (and any other listed). When finished, it reports the list back by mapping virtual machine names to hostnames as provided in HOSTLIST. If you see the full list of hostnames as a result, then everything is going well. If otherwise you are having a partial or empty list, you have to check parameters. You can even test "reset", "off" and "on" commands, to test (carefully!) the full chain. E.g. VI_SERVER=10.1.1.1 \ VI_CREDSTORE=/etc/vicredentials.xml \ HOSTLIST="hostname1=vmname1;hostname2=vmname2" \ RESETPOWERON=0 \ /usr/lib/stonith/plugins/external/vcenter reset hostname2 In the above examples the referring infrastructure is a vCenter with several ESXi nodes. Server IP and credentials are referred to vCenter. 5. CRM configuration -------------------- The following is a sample procedure to setup STONITH for an HA 2-node cluster (replace all the provided sample values): crm configure primitive vfencing stonith::external/vcenter params \ VI_SERVER="10.1.1.1" VI_CREDSTORE="/etc/vicredentials.xml" \ HOSTLIST="hostname1=vmname1;hostname2=vmname2" RESETPOWERON="0" \ op monitor interval="60s" crm configure clone Fencing vfencing crm configure property stonith-enabled="true" Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README.wti_mpc0000644000000000000000000000664212120057602026047 0ustar00usergroup00000000000000STONITH module for WTI MPC -------------------------- ****Introduction. wti_mpc module uses snmp for controlling the MPC power distribution unit. It has been tested with MPC-8H and MPC-18H and should be compatible with the whole MPC series: * MPC-20* * MPC-16* * MPC-18* * MPC-8* ****Unit configuration. wti_mpc STONITH modules uses SNMP v1, therefore it should be configured on the device side. To do so, you should login to device, go to "Network configuration" (/N), select "SNMP access" (25) and turn it on (enable/1). At the SNMP access screen set "Version" (2) to "V1/V2 Only", set "Read only" (3) to "No and set any "Community" (10) you want. You may also set other options as you need. You may check your setup by issuing the following command: snmpwalk -v1 -c .1.3.6.1.2.1.1.1.0 and result should be something like this: SNMPv2-MIB::sysDescr.0 = STRING: Linux 85.195.135.236 2.4.18_mvl30-cllf #1991 Sun Mar 16 14:39:29 PST 2008 ppc ****Plugin configuration. Plugin declares the following configuration variables: *ipaddr - ip address or hostname of a MPC unit. *port - ip port, should be 161, as MPC listens for incoming SNMP packets on that port. It is made for future use actually. *community - Community that you've specified on previous step. *mib_version - Should be 3 for MPC devices with firmware version 1.62 and later. 1 is for firmware version 1.44 and below. 2 is unused right now, if you have device, with mib V2 feel free to contact me and I'll add it. ****MIB version issue WTI guys have several time changed OIDs, used by MPC devices. I own two types of the devices: *With firmware v 1.44 which is compatible with MIB version 1 *With firmware v 1.62 which is compatible with MIB version 3 I suppose there are exist MIB v2, but i cannot find it and I'd not able to test it. Anyway, this plugin supports both V1 and V3 versions, and the correct version is selected by the "mib-version" configuration parameter. Default value is "1", so if you do not specify this parameter or assign a unsupported value to it, it will fall back to mib version 1. ****Outlets and groups MPC devices forces unique names of the outlets. This is a big problem for STONITH plugin, cause it uses nodes unames as outlet names, so in case you have a node with several power plugs, you should have set the node uname as name of all the plugs. The MPC device simply doesn't allows this. So, this plugin works with a GROUPS instead of a PLUGS. You may give any unique names for your physical outlets on the MPC, but you MUST create a plug group, name it using node's uname and include plugs, corresponding to that particular node to this group. It should be done even for node with single power supply. Some example: Let's pretend you have a node "atest", with two power cords, connected to plugs A1 and B1. You have to create a group ("Plug grouping parameters" (/G) -> Add Plug Group to directory (2)), name it "atest" ("Plug Group Name (1)) and assign plugs A1 and B1 to that group ("Plug access" (2)). Now save your configuration and try to retrieve host list: stonith -t wti_mpc ipaddr= port=161 community= mib-version= -l result should be: atest ------------------ (C) Denis Chapligin , SatGate, 2009 Reusable-Cluster-Components-glue--3cff550e1084/doc/stonith/README_kdumpcheck.txt0000644000000000000000000001560612120057602027422 0ustar00usergroup00000000000000 Kdump check STONITH plugin "kdumpcheck" 1. Introduction This plugin's purpose is to avoid STONITH for a node which is doing kdump. It confirms whether the node is doing kdump or not when STONITH reset or off operation is executed. If the target node is doing kdump, this plugin considers that STONITH succeeded. If not, it considers that STONITH failed. NOTE: This plugin has no ability to shutdown or startup a node. So it has to be used with other STONITH plugin. Then, when this plugin failed, the next plugin which can kill a node is executed. NOTE: This plugin works only on Linux. 2. The way to check When STONITH reset or off is executed, kdumpcheck connects to the target node, and checks the size of /proc/vmcore. It judges that the target node is _not_ doing kdump when the size of /proc/vmcore on the node is zero, or the file doesn't exist. Then kdumpcheck returns "STONITH failed" to stonithd, and the next plugin is executed. 3. Expanding mkdumprd This plugin requires non-root user and ssh connection even on 2nd kernel. So, you need to apply mkdumprd_for_kdumpcheck.patch to /sbin/mkdumprd. This patch is tested with mkdumprd version 5.0.39. The patch adds the following functions: i) Start udevd with specified .rules files. ii) Bring the specified network interface up. iii) Start sshd. iv) Add the specified user to the 2nd kernel. The user is to check whether the node is doing kdump or not. v) Execute sync command after dumping. NOTE: i) to iv) expandings are only for the case that filesystem partition is specified as the location where the vmcore should be dumped. 4. Parameters kdumpcheck's parameters are the following. hostlist : The list of hosts that the STONITH device controls. delimiter is "," or " ". indispensable setting. (default:none) identity_file: a full-path of the private key file for the user who checks doing kdump. (default: $HOME/.ssh/id_rsa, $HOME/.ssh/id_dsa and $HOME/.ssh/identity) NOTE: To execute this plugin first, set the highest priority to this plugin in all STONITH resources. 5. How to Use To use this tool, do the following steps at all nodes in the cluster. 1) Add an user to check doing kdump. ex.) # useradd kdumpchecker # passwd kdumpchecker 2) Allow passwordless login from the node which will do STONITH to all target nodes for the user added at step 1). ex.) $ cd $ mkdir .ssh $ chmod 700 .ssh $ cd .ssh $ ssh-keygen (generate authentication keys with empty passphrase) $ scp id_rsa.pub kdumpchecker@target_node:"~/.ssh/." $ ssh kdumpchecker@target_node $ cd ~/.ssh $ cat id_rsa.pub >> authorized_keys $ chmod 600 autorized_keys $ rm id_rsa.pub 3) Limit the command that the user can execute. Describe the following commands in a line at the head of the user's public key in target node's authorized_keys file. [command="test -s /proc/vmcore"] And describe some options (like no-pty, no-port-forwarding and so on) according to your security policy. ex.) $ vi ~/.ssh/authorized_keys command="test -s /proc/vmcore",no-port-forwarding,no-X11-forwarding, no-agent-forwarding,no-pty ssh-rsa AAA..snip..== kdumpchecker@node1 4) Add settings in /etc/kdump.conf. network_device : network interface name to check doing kdump. indispensable setting. (default: none) kdump_check_user : user name to check doing kdump. specify non-root user. (default: "kdumpchecker") udev_rules : .rules files' names. specify if you use udev for mapping devices. specified files have to be in /etc/udev/rules.d/. you can specify two or more files. delimiter is "," or " ". (default: none) ex.) # vi /etc/kdump.conf ext3 /dev/sda1 network_device eth0 kdump_check_user kdumpchecker udev_rules 10-if.rules 5) Apply the patch to /sbin/mkdumprd. # cd /sbin # patch -p 1 < mkdumprd_for_kdumpcheck.patch 6) Restart kdump service. # service kdump restart 7) Describe cib.xml to set STONITH plugin. (See "2. Parameters" and "6. Appendix") 6. Appendix A sample cib.xml. Reusable-Cluster-Components-glue--3cff550e1084/hb_report/Makefile.am0000644000000000000000000000174412120057602025307 0ustar00usergroup00000000000000# # heartbeat: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in hanoarchdir = $(datadir)/$(PACKAGE_NAME) hanoarch_SCRIPTS = combine-logs.pl hanoarch_DATA = utillib.sh ha_cf_support.sh openais_conf_support.sh sbin_SCRIPTS = hb_report Reusable-Cluster-Components-glue--3cff550e1084/hb_report/combine-logs.pl0000755000000000000000000000745112120057602026172 0ustar00usergroup00000000000000#!/usr/bin/perl # # combine-logs v1.0 # # Copyright (c) 1999 Steven J. Madsen. All rights reserved. # # Combines multiple syslog-format logs into a single chronological log. Very # handy for syslog report generators such as cksyslog. # # usage: combine-logs [...] # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Note by Dejan Muhamedagic # # This program was downloaded from # http://www.moonglade.com/syslog/combine-logs-1.0.tar.gz # $debugging = 0; # Open all of the logs. $handle = "fh00"; foreach $file (@ARGV) { $handle++; open $handle, $file || die "Could not open $file: $!\n"; push @fh, $handle; } # Get the first line from each of the files. $i = 0; foreach $handle (@fh) { $current_line[$i++] = get_next_line($handle); } # Process the logs. while (1) { $first = 0; for ($i = 1; $i < @fh; $i++) { if (first_entry($current_line[$first], $current_line[$i])) { $first = $i; } } # Fall out if the entry isn't defined (no more entries to print). last if !defined($current_line[$first]); # Print the entry and get the next line from that log. print $current_line[$first]; $current_line[$first] = get_next_line($fh[$first]); } # Gets the next line from the provided file handle. sub get_next_line() { my($handle) = @_; my($line); while ($line = <$handle>) { print " read $line" if $debugging; # Weed out useless "last message repeated" messages. next if $line =~ m/last message repeated \d+ times$/; # Fall out if the line passes the above tests. last; } return $line; } # Determines which syslog-style log entry comes first. If $a comes first, # the function returns 0. If $b comes first, the function returns 1. sub first_entry() { my($a, $b) = @_; print " \$a=$a \$b=$b" if $debugging; return 0 if !defined($b); return 1 if !defined($a); my(%month) = (Jan => 0, Feb => 1, Mar => 2, Apr => 3, May => 4, Jun => 5, Jul => 6, Aug => 7, Sep => 8, Oct => 9, Nov => 10, Dec => 11); my($a_month, $a_day, $a_hour, $a_minute, $a_second) = $a =~ /^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/; my($b_month, $b_day, $b_hour, $b_minute, $b_second) = $b =~ /^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/; print " a: $a_month $a_day $a_hour:$a_minute:$a_second\n" if $debugging; print " b: $b_month $b_day $b_hour:$b_minute:$b_second\n" if $debugging; # Strictly speaking, Jan comes before Dec, but in the case that we are # comparing exactly those two, we consider Jan to come later. In the # context of a log, this probably means a new year. return 0 if $a_month eq "Dec" && $b_month eq "Jan"; return 1 if $a_month eq "Jan" && $b_month eq "Dec"; # All other comparisons are as you'd expect. if ($a_month ne $b_month) { return $month{$a_month} > $month{$b_month}; } if ($a_day ne $b_day) { return $a_day > $b_day; } if ($a_hour ne $b_hour) { return $a_hour > $b_hour; } if ($a_minute ne $b_minute) { return $a_minute > $b_minute; } if ($a_second ne $b_second) { return $a_second > $b_second; } # They have identical times, so just pick the first one. return 0; } Reusable-Cluster-Components-glue--3cff550e1084/hb_report/ha_cf_support.sh0000644000000000000000000000456612120057602026450 0ustar00usergroup00000000000000 # Copyright (C) 2007 Dejan Muhamedagic # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Stack specific part (heartbeat) # ha.cf/logd.cf parsing # getcfvar() { [ -f "$CONF" ] || return sed 's/#.*//' < $CONF | grep -w "^$1" | sed 's/^[^[:space:]]*[[:space:]]*//' } iscfvarset() { test "`getcfvar $1`" } iscfvartrue() { getcfvar "$1" | egrep -qsi "^(true|y|yes|on|1)" } uselogd() { iscfvartrue use_logd && return 0 # if use_logd true iscfvarset logfacility || iscfvarset logfile || iscfvarset debugfile || return 0 # or none of the log options set false } get_hb_logvars() { # unless logfacility is set to none, heartbeat/ha_logd are # going to log through syslog HA_LOGFACILITY=`getcfvar logfacility` [ "" = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=$DEFAULT_HA_LOGFACILITY [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" HA_LOGFILE=`getcfvar logfile` HA_DEBUGFILE=`getcfvar debugfile` } getlogvars() { HA_LOGFACILITY=${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY} HA_LOGLEVEL="info" cfdebug=`getcfvar debug` # prefer debug level if set isnumber $cfdebug || cfdebug="" [ "$cfdebug" ] && [ $cfdebug -gt 0 ] && HA_LOGLEVEL="debug" if uselogd; then [ -f "$LOGD_CF" ] || { debug "logd used but logd.cf not found: using defaults" return # no configuration: use defaults } debug "reading log settings from $LOGD_CF" get_logd_logvars else debug "reading log settings from $CONF" get_hb_logvars fi } cluster_info() { echo "heartbeat version: `$HA_BIN/heartbeat -V`" } essential_files() { cat< # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # . @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs HA_NOARCHBIN=@datadir@/@PACKAGE_NAME@ . $HA_NOARCHBIN/utillib.sh unset LANG export LC_ALL=POSIX PROG=`basename $0` # the default syslog facility is not (yet) exported by heartbeat # to shell scripts # DEFAULT_HA_LOGFACILITY="daemon" export DEFAULT_HA_LOGFACILITY LOGD_CF=`findlogdcf @sysconfdir@ $HA_DIR` export LOGD_CF : ${SSH_OPTS="-o StrictHostKeyChecking=no -o EscapeChar=none"} LOG_PATTERNS="CRIT: ERROR:" # PEINPUTS_PATT="peng.*PEngine Input stored" # Important events # # Patterns format: # title extended_regexp # NB: don't use spaces in titles or regular expressions! EVENT_PATTERNS=" membership crmd.*ccm_event.*(NEW|LOST)|pcmk_peer_update.*(lost|memb): quorum crmd.*crm_update_quorum:.Updating.quorum.status|crmd.*ais.disp.*quorum.(lost|ac?quir) pause Process.pause.detected resources lrmd.*rsc:(start|stop) stonith crmd.*te_fence_node.*Exec|stonith-ng.*log_oper.*reboot|stonithd.*(requests|(Succeeded|Failed).to.STONITH|result=) start_stop Configuration.validated..Starting.heartbeat|Corosync.Cluster.Engine|Executive.Service.RELEASE|crm_shutdown:.Requesting.shutdown|pcmk_shutdown:.Shutdown.complete " init_tmpfiles # # the instance where user runs hb_report is the master # the others are slaves # if [ x"$1" = x__slave ]; then SLAVE=1 fi usage() { cat</dev/null ls -rt `2>/dev/null find /var/lib -name pengine -type d | sed 's,$,/*.last,'` | tail -1) if [ -f "$lastf" ]; then PE_STATE_DIR=`dirname $lastf` else for p in pacemaker/pengine pengine heartbeat/pengine; do if [ -d $localstatedir/$p ]; then debug "setting PE_STATE_DIR to $localstatedir/$p" PE_STATE_DIR=$localstatedir/$p break fi done fi } get_cib_dir2() { # CIB # HA_VARLIB is normally set to {localstatedir}/heartbeat local localstatedir localstatedir=`dirname $HA_VARLIB` for p in pacemaker/cib heartbeat/crm; do if [ -f $localstatedir/$p/cib.xml ]; then debug "setting CIB_DIR to $localstatedir/$p" CIB_DIR=$localstatedir/$p break fi done } get_crm_daemon_dir() { # CRM_DAEMON_DIR local libdir libdir=`dirname $HA_BIN` for p in pacemaker heartbeat; do if [ -x $libdir/$p/crmd ]; then debug "setting CRM_DAEMON_DIR to $libdir/$p" CRM_DAEMON_DIR=$libdir/$p break fi done if [ ! -d "$CRM_DAEMON_DIR" ]; then fatal "cannot find pacemaker daemon directory!" fi } compatibility_pcmk() { get_crm_daemon_dir get_pe_state_dir || get_pe_state_dir2 get_cib_dir || get_cib_dir2 debug "setting PCMK_LIB to `dirname $CIB_DIR`" PCMK_LIB=`dirname $CIB_DIR` # PTEST PTEST=`echo_ptest_tool` export PE_STATE_DIR CIB_DIR CRM_DAEMON_DIR PCMK_LIB PTEST } # # find log files # logmark() { logger -p $* debug "run: logger -p $*" } # # first try syslog files, if none found then use the # logfile/debugfile settings # findlog() { local logf="" if [ "$HA_LOGFACILITY" ]; then logf=`findmsg $UNIQUE_MSG | awk '{print $1}'` fi if [ -f "$logf" ]; then echo $logf else echo ${HA_DEBUGFILE:-$HA_LOGFILE} [ "${HA_DEBUGFILE:-$HA_LOGFILE}" ] && debug "will try with ${HA_DEBUGFILE:-$HA_LOGFILE}" fi } # # find log slices # find_decompressor() { if echo $1 | grep -qs 'bz2$'; then echo "bzip2 -dc" elif echo $1 | grep -qs 'gz$'; then echo "gzip -dc" else echo "cat" fi } # # check if the log contains a piece of our segment # is_our_log() { local logf=$1 local from_time=$2 local to_time=$3 local cat=`find_decompressor $logf` local first_time="`$cat $logf | head -10 | find_first_ts`" local last_time="`$cat $logf | tail -10 | tac | find_first_ts`" if [ x = "x$first_time" -o x = "x$last_time" ]; then return 0 # skip (empty log?) fi if [ $from_time -gt $last_time ]; then # we shouldn't get here anyway if the logs are in order return 2 # we're past good logs; exit fi if [ $from_time -ge $first_time ]; then return 3 # this is the last good log fi # have to go further back if [ $to_time -eq 0 -o $to_time -ge $first_time ]; then return 1 # include this log else return 0 # don't include this log fi } # # go through archived logs (timewise backwards) and see if there # are lines belonging to us # (we rely on untouched log files, i.e. that modify time # hasn't been changed) # arch_logs() { local next_log local logf=$1 local from_time=$2 local to_time=$3 # look for files such as: ha-log-20090308 or # ha-log-20090308.gz (.bz2) or ha-log.0, etc ls -t $logf $logf*[0-9z] 2>/dev/null | while read next_log; do is_our_log $next_log $from_time $to_time case $? in 0) ;; # noop, continue 1) echo $next_log # include log and continue debug "found log $next_log" ;; 2) break;; # don't go through older logs! 3) echo $next_log # include log and continue debug "found log $next_log" break ;; # don't go through older logs! esac done } # # print part of the log # print_log() { local cat=`find_decompressor $1` $cat $1 } print_logseg() { if test -x $HA_NOARCHBIN/print_logseg; then $HA_NOARCHBIN/print_logseg "$1" "$2" "$3" return fi local logf=$1 local from_time=$2 local to_time=$3 local tmp sourcef # uncompress to a temp file (if necessary) local cat=`find_decompressor $logf` if [ "$cat" != "cat" ]; then tmp=`mktemp` || fatal "disk full" add_tmpfiles $tmp $cat $logf > $tmp || fatal "disk full" sourcef=$tmp else sourcef=$logf tmp="" fi if [ "$from_time" = 0 ]; then FROM_LINE=1 else FROM_LINE=`findln_by_time $sourcef $from_time` fi if [ -z "$FROM_LINE" ]; then warning "couldn't find line for time $from_time; corrupt log file?" return fi TO_LINE="" if [ "$to_time" != 0 ]; then TO_LINE=`findln_by_time $sourcef $to_time` if [ -z "$TO_LINE" ]; then warning "couldn't find line for time $to_time; corrupt log file?" return fi fi dumplog $sourcef $FROM_LINE $TO_LINE debug "including segment [$FROM_LINE-$TO_LINE] from $logf" } # # print some log info (important for crm history) # loginfo() { local logf=$1 local fake=$2 local nextpos=`python -c "f=open('$logf');f.seek(0,2);print f.tell()+1"` if [ "$fake" ]; then echo "synthetic:$logf $nextpos" else echo "$logf $nextpos" fi } # # find log/set of logs which are interesting for us # dumplogset() { local logf=$1 local from_time=$2 local to_time=$3 local logf_set=`arch_logs $logf $from_time $to_time` if [ x = "x$logf_set" ]; then return fi local num_logs=`echo "$logf_set" | wc -l` local oldest=`echo $logf_set | awk '{print $NF}'` local newest=`echo $logf_set | awk '{print $1}'` local mid_logfiles=`echo $logf_set | awk '{for(i=NF-1; i>1; i--) print $i}'` # the first logfile: from $from_time to $to_time (or end) # logfiles in the middle: all # the last logfile: from beginning to $to_time (or end) case $num_logs in 1) print_logseg $newest $from_time $to_time;; *) print_logseg $oldest $from_time 0 for f in $mid_logfiles; do print_log $f debug "including complete $f logfile" done print_logseg $newest 0 $to_time ;; esac } # # cts_findlogseg finds lines for the CTS test run (FROM_LINE and # TO_LINE) and then extracts the timestamps to get FROM_TIME and # TO_TIME # cts_findlogseg() { local testnum=$1 local logf=$2 if [ "x$logf" = "x" ]; then logf=`findmsg "Running test.*\[ *$testnum\]" | awk '{print $1}'` fi getstampproc=`find_getstampproc < $logf` export getstampproc # used by linetime FROM_LINE=`grep -n "Running test.*\[ *$testnum\]" $logf | tail -1 | sed 's/:.*//'` if [ -z "$FROM_LINE" ]; then warning "couldn't find line for CTS test $testnum; corrupt log file?" exit 1 else FROM_TIME=`linetime $logf $FROM_LINE` fi TO_LINE=`grep -n "Running test.*\[ *$(($testnum+1))\]" $logf | tail -1 | sed 's/:.*//'` [ "$TO_LINE" -a $FROM_LINE -lt $TO_LINE ] || TO_LINE=`wc -l < $logf` TO_TIME=`linetime $logf $TO_LINE` debug "including segment [$FROM_LINE-$TO_LINE] from $logf" dumplog $logf $FROM_LINE $TO_LINE } # # this is how we pass environment to other hosts # dumpenv() { cat</dev/null } findsshuser() { local n u rc local ssh_s ssh_user="__undef" try_user_list failed_nodes="" try_user_list="\"\" $TRY_SSH" for n in $NODES; do rc=1 [ "$n" = "$WE" ] && continue for u in $try_user_list; do if [ "$u" != '""' ]; then ssh_s=$u@$n else ssh_s=$n fi if testsshconn $ssh_s; then debug "ssh $ssh_s OK" ssh_user="$u" try_user_list="$u" # we support just one user rc=0 break else debug "ssh $ssh_s failed" fi done [ $rc = 1 ] && failed_nodes="$failed_nodes $n" done [ -n "$failed_nodes" ] && warning "ssh to node(s) $failed_nodes does not work" # drop nodes we cannot reach NODES=`for n in $failed_nodes $NODES; do echo $n; done | sort | uniq -u` if [ "$ssh_user" = "__undef" ]; then return 1 fi if [ "$ssh_user" != '""' ]; then echo $ssh_user fi return 0 } # # the usual stuff # getbacktraces() { local f bf flist flist=$( for f in `find_files "$CORES_DIRS" $1 $2`; do bf=`basename $f` test `expr match $bf core` -gt 0 && echo $f done) [ "$flist" ] && { getbt $flist > $3 debug "found backtraces: $flist" } } pe2dot() { local pef=`basename $1` local dotf=`basename $pef .bz2`.dot test -z "$PTEST" && return ( cd `dirname $1` $PTEST -D $dotf -x $pef >/dev/null 2>&1 ) } getpeinputs() { local pe_dir flist local f pe_dir=$PE_STATE_DIR debug "looking for PE files in $pe_dir" flist=$( find_files $pe_dir $1 $2 | grep -v '[.]last$' ) [ "$flist" ] && { mkdir $3/`basename $pe_dir` ( cd $3/`basename $pe_dir` for f in $flist; do ln -s $f done ) debug "found `echo $flist | wc -w` pengine input files in $pe_dir" } if [ `echo $flist | wc -w` -le 20 ]; then for f in $flist; do pe2dot $3/`basename $pe_dir`/`basename $f` done else debug "too many PE inputs to create dot files" fi } getratraces() { local trace_dir flist local f trace_dir=$HA_VARLIB/trace_ra test -d "$trace_dir" || return 0 debug "looking for RA trace files in $trace_dir" flist=$(find_files $trace_dir $1 $2 | sed "s,`dirname $trace_dir`/,,g") [ "$flist" ] && { tar -cf - -C `dirname $trace_dir` $flist | tar -xf - -C $3 debug "found `echo $flist | wc -w` RA trace files in $trace_dir" } } touch_DC_if_dc() { local dc dc=`crmadmin -D 2>/dev/null | awk '{print $NF}'` if [ "$WE" = "$dc" ]; then touch $1/DC fi } corosync_blackbox() { local from_time=$1 local to_time=$2 local outf=$3 local inpf inpf=`find_files /var/lib/corosync $from_time $to_time | grep -w fdata` if [ -f "$inpf" ]; then corosync-fplay > $outf touch -r $inpf $outf fi } getconfigurations() { local conf local dest=$1 for conf in $CONFIGURATIONS; do if [ -f $conf ]; then cp -p $conf $dest elif [ -d $conf ]; then ( cd `dirname $conf` && tar cf - `basename $conf` | (cd $dest && tar xf -) ) fi done } # # some basic system info and stats # sys_info() { cluster_info hb_report -V # our info echo "resource-agents: `grep 'Build version:' @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs`" crm_info pkg_ver $PACKAGES echo "Platform: `uname`" echo "Kernel release: `uname -r`" echo "Architecture: `uname -m`" [ `uname` = Linux ] && echo "Distribution: `distro`" } sys_stats() { set -x uname -n uptime ps axf ps auxw top -b -n 1 ifconfig -a ip addr list netstat -i arp -an test -d /proc && { cat /proc/cpuinfo } lsscsi lspci mount # df can block, run in background, allow for 5 seconds (!) local maxcnt=5 df & while kill -0 $! >/dev/null 2>&1; do sleep 1 if [ $maxcnt -le 0 ]; then warning "df appears to be hanging, continuing without it" break fi maxcnt=$((maxcnt-1)) done set +x } time_status() { date ntpdc -pn } dlm_dump() { if which dlm_tool >/dev/null 2>&1 ; then echo NOTICE - Lockspace overview: dlm_tool ls dlm_tool ls | grep name | while read X N ; do echo NOTICE - Lockspace $N: dlm_tool lockdump $N done echo NOTICE - Lockspace history: dlm_tool dump fi } # # replace sensitive info with '****' # sanitize() { local f rc for f in $1/$B_CONF; do [ -f "$f" ] && sanitize_one $f done rc=0 for f in $1/$CIB_F $1/pengine/*; do if [ -f "$f" ]; then if [ "$DO_SANITIZE" ]; then sanitize_one $f else test_sensitive_one $f && rc=1 fi fi done [ $rc -ne 0 ] && { warning "some PE or CIB files contain possibly sensitive data" warning "you may not want to send this report to a public mailing list" } } # # remove duplicates if files are same, make links instead # consolidate() { for n in $NODES; do if [ -f $1/$2 ]; then rm $1/$n/$2 else mv $1/$n/$2 $1 fi ln -s ../$2 $1/$n done } # # some basic analysis of the report # checkcrmvfy() { for n in $NODES; do if [ -s $1/$n/$CRM_VERIFY_F ]; then echo "WARN: crm_verify reported warnings at $n:" cat $1/$n/$CRM_VERIFY_F fi done } checkbacktraces() { for n in $NODES; do [ -s $1/$n/$BT_F ] && { echo "WARN: coredumps found at $n:" egrep 'Core was generated|Program terminated' \ $1/$n/$BT_F | sed 's/^/ /' } done } checkpermissions() { for n in $NODES; do if [ -s $1/$n/$PERMISSIONS_F ]; then echo "WARN: problem with permissions/ownership at $n:" cat $1/$n/$PERMISSIONS_F fi done } checklogs() { local logs pattfile l n logs=$(find $1 -name $HALOG_F; for l in $EXTRA_LOGS; do find $1/* -name `basename $l`; done) [ "$logs" ] || return pattfile=`mktemp` || fatal "cannot create temporary files" add_tmpfiles $pattfile for p in $LOG_PATTERNS; do echo "$p" done > $pattfile echo "" echo "Log patterns:" for n in $NODES; do cat $logs | grep -f $pattfile done } # # check if files have same content in the cluster # cibdiff() { local d1 d2 d1=`dirname $1` d2=`dirname $2` if [ -f $d1/RUNNING -a -f $d2/RUNNING ] || [ -f $d1/STOPPED -a -f $d2/STOPPED ]; then if which crm_diff > /dev/null 2>&1; then crm_diff -c -n $1 -o $2 else info "crm_diff(8) not found, cannot diff CIBs" fi else echo "can't compare cibs from running and stopped systems" fi } txtdiff() { diff -u $1 $2 } diffcheck() { [ -f "$1" ] || { echo "$1 does not exist" return 1 } [ -f "$2" ] || { echo "$2 does not exist" return 1 } case `basename $1` in $CIB_F) cibdiff $1 $2;; $B_CONF) txtdiff $1 $2;; # confdiff? *) txtdiff $1 $2;; esac } analyze_one() { local rc node0 n rc=0 node0="" for n in $NODES; do if [ "$node0" ]; then diffcheck $1/$node0/$2 $1/$n/$2 rc=$(($rc+$?)) else node0=$n fi done return $rc } analyze() { local f flist flist="$HOSTCACHE $MEMBERSHIP_F $CIB_F $CRM_MON_F $B_CONF logd.cf $SYSINFO_F" for f in $flist; do printf "Diff $f... " ls $1/*/$f >/dev/null 2>&1 || { echo "no $1/*/$f :/" continue } if analyze_one $1 $f; then echo "OK" [ "$f" != $CIB_F ] && consolidate $1 $f else echo "" fi done checkcrmvfy $1 checkbacktraces $1 checkpermissions $1 checklogs $1 } events_all() { local Epatt title p Epatt=`echo "$EVENT_PATTERNS" | while read title p; do [ -n "$p" ] && echo -n "|$p"; done | sed 's/.//' ` grep -E "$Epatt" $1 } events() { local destdir n destdir=$1 if [ -f $destdir/$HALOG_F ]; then events_all $destdir/$HALOG_F > $destdir/events.txt for n in $NODES; do awk "\$4==\"$n\"" $destdir/events.txt > $destdir/$n/events.txt done else for n in $NODES; do [ -f $destdir/$n/$HALOG_F ] || continue events_all $destdir/$n/$HALOG_F > $destdir/$n/events.txt done fi } # # if there is no central log, let's combine logs from individual # nodes; the ordering may not be perfect, but then centralized # logging doesn't guarantee the order of messages either # (network delay, machine load) # combine_logs() { local destdir destdir=$1 test $NODECNT -gt 1 || return test -x $HA_NOARCHBIN/combine-logs.pl || warning "cannot combine logs: no $HA_NOARCHBIN/combine-logs.pl" $HA_NOARCHBIN/combine-logs.pl $destdir/*/$HALOG_F > $destdir/$HALOG_F loginfo $destdir/$HALOG_F combined > $destdir/$HALOG_F.info $HA_NOARCHBIN/combine-logs.pl $destdir/*/events.txt > $destdir/events.txt } # # description template, editing, and other notes # mktemplate() { cat< $outf else warning "no log at $WE" fi return fi if [ "$cnt" ] && [ $cnt -gt 1 -a $cnt -eq $NODECNT ]; then MASTER_IS_HOSTLOG=1 info "found the central log!" fi if [ "$NO_str2time" ]; then warning "a log found; but we cannot slice it" warning "please install the perl Date::Parse module" elif [ "$CTS" ]; then cts_findlogseg $CTS $HA_LOG > $outf else getstampproc=`find_getstampproc < $HA_LOG` if [ "$getstampproc" ]; then export getstampproc # used by linetime dumplogset $HA_LOG $FROM_TIME $TO_TIME > $outf && loginfo $HA_LOG > $outf.info || fatal "disk full" else warning "could not figure out the log format of $HA_LOG" fi fi } # # get all other info (config, stats, etc) # collect_info() { local getstampproc l sys_info > $WORKDIR/$SYSINFO_F 2>&1 & sys_stats > $WORKDIR/$SYSSTATS_F 2>&1 & getconfig $WORKDIR getpeinputs $FROM_TIME $TO_TIME $WORKDIR & crmconfig $WORKDIR & touch_DC_if_dc $WORKDIR & getbacktraces $FROM_TIME $TO_TIME $WORKDIR/$BT_F getconfigurations $WORKDIR check_perms > $WORKDIR/$PERMISSIONS_F 2>&1 dlm_dump > $WORKDIR/$DLM_DUMP_F 2>&1 time_status > $WORKDIR/$TIME_F 2>&1 corosync_blackbox $FROM_TIME $TO_TIME $WORKDIR/$COROSYNC_RECORDER_F getratraces $FROM_TIME $TO_TIME $WORKDIR wait sanitize $WORKDIR for l in $EXTRA_LOGS; do [ "$NO_str2time" ] && break [ ! -f "$l" ] && continue if [ "$l" = "$HA_LOG" -a "$l" != "$HALOG_F" ]; then ln -s $HALOG_F $WORKDIR/`basename $l` continue fi getstampproc=`find_getstampproc < $l` if [ "$getstampproc" ]; then export getstampproc # used by linetime dumplogset $l $FROM_TIME $TO_TIME > $WORKDIR/`basename $l` && loginfo $l > $WORKDIR/`basename $l`.info || fatal "disk full" else warning "could not figure out the log format of $l" fi done } finalword() { if [ "$COMPRESS" = "1" ]; then echo "The report is saved in $DESTDIR/$DEST.tar$COMPRESS_EXT" else echo "The report is saved in $DESTDIR/$DEST" fi echo " " echo "Thank you for taking time to create this report." } [ $# -eq 0 ] && usage # check for the major prereq for a) parameter parsing and b) # parsing logs # NO_str2time="" t=`str2time "12:00"` if [ "$t" = "" ]; then NO_str2time=1 is_collector || fatal "please install the perl Date::Parse module" fi WE=`uname -n` # who am i? tmpdir=`mktemp -t -d .hb_report.workdir.XXXXXX` || fatal "disk full" add_tmpfiles $tmpdir WORKDIR=$tmpdir # # part 1: get and check options; and the destination # if ! is_collector; then setvarsanddefaults userargs="$@" DESTDIR=. DEST="hb_report-"`date +"%a-%d-%b-%Y"` while getopts f:t:l:u:p:L:e:E:n:MSDCZAVsvhd o; do case "$o" in h) usage;; V) version;; f) if echo "$OPTARG" | grep -qs '^cts:'; then FROM_TIME=0 # to be calculated later CTS=`echo "$OPTARG" | sed 's/cts://'` DEST="cts-$CTS-"`date +"%a-%d-%b-%Y"` else FROM_TIME=`str2time "$OPTARG"` chktime "$FROM_TIME" "$OPTARG" fi ;; t) TO_TIME=`str2time "$OPTARG"` chktime "$TO_TIME" "$OPTARG" ;; n) NODES_SOURCE=user USER_NODES="$USER_NODES $OPTARG" ;; u) SSH_USER="$OPTARG";; l) HA_LOG="$OPTARG";; e) EDITOR="$OPTARG";; p) SANITIZE="$SANITIZE $OPTARG";; s) DO_SANITIZE="1";; L) LOG_PATTERNS="$LOG_PATTERNS $OPTARG";; S) NO_SSH=1;; D) NO_DESCRIPTION=1;; C) : ;; Z) FORCE_REMOVE_DEST=1;; M) EXTRA_LOGS="";; E) EXTRA_LOGS="$EXTRA_LOGS $OPTARG";; A) USER_CLUSTER_TYPE="openais";; v) VERBOSITY=$((VERBOSITY + 1));; d) COMPRESS="";; [?]) usage short;; esac done shift $(($OPTIND-1)) [ $# -gt 1 ] && usage short set_dest $* [ "$FROM_TIME" ] || usage short WORKDIR=$tmpdir/$DEST else WORKDIR=$tmpdir/$DEST/$WE fi mkdir -p $WORKDIR [ -d $WORKDIR ] || no_dir if is_collector; then cat > $WORKDIR/.env . $WORKDIR/.env fi [ $VERBOSITY -gt 1 ] && { is_collector || { info "high debug level, please read debug.out" } PS4='+ ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: ' if echo "$SHELL" | grep bash > /dev/null && [ ${BASH_VERSINFO[0]} = "4" ]; then exec 3>>$WORKDIR/debug.out BASH_XTRACEFD=3 else exec 2>>$WORKDIR/debug.out fi set -x } compatibility_pcmk # allow user to enforce the cluster type # if not, then it is found out on _all_ nodes if [ -z "$USER_CLUSTER_TYPE" ]; then CLUSTER_TYPE=`get_cluster_type` else CLUSTER_TYPE=$USER_CLUSTER_TYPE fi # the very first thing we must figure out is which cluster # stack is used CORES_DIRS="`2>/dev/null ls -d $HA_VARLIB/cores $PCMK_LIB/cores | uniq`" PACKAGES="pacemaker libpacemaker3 pacemaker-pygui pacemaker-pymgmt pymgmt-client openais libopenais2 libopenais3 corosync libcorosync4 resource-agents cluster-glue libglue2 ldirectord heartbeat heartbeat-common heartbeat-resources libheartbeat2 ocfs2-tools ocfs2-tools-o2cb ocfs2console ocfs2-kmp-default ocfs2-kmp-pae ocfs2-kmp-xen ocfs2-kmp-debug ocfs2-kmp-trace drbd drbd-kmp-xen drbd-kmp-pae drbd-kmp-default drbd-kmp-debug drbd-kmp-trace drbd-heartbeat drbd-pacemaker drbd-utils drbd-bash-completion drbd-xen lvm2 lvm2-clvm cmirrord libdlm libdlm2 libdlm3 hawk ruby lighttpd kernel-default kernel-pae kernel-xen glibc " case "$CLUSTER_TYPE" in openais) CONF=/etc/corosync/corosync.conf # corosync? if test -f $CONF; then CORES_DIRS="$CORES_DIRS /var/lib/corosync" else CONF=/etc/ais/openais.conf CORES_DIRS="$CORES_DIRS /var/lib/openais" fi CF_SUPPORT=$HA_NOARCHBIN/openais_conf_support.sh MEMBERSHIP_TOOL_OPTS="" unset HOSTCACHE HB_UUID_F ;; heartbeat) CONF=$HA_CF CF_SUPPORT=$HA_NOARCHBIN/ha_cf_support.sh MEMBERSHIP_TOOL_OPTS="-H" ;; esac B_CONF=`basename $CONF` if test -f "$CF_SUPPORT"; then . $CF_SUPPORT else fatal "no stack specific support: $CF_SUPPORT" fi if [ "x$CTS" = "x" ] || is_collector; then getlogvars debug "log settings: facility=$HA_LOGFACILITY logfile=$HA_LOGFILE debugfile=$HA_DEBUGFILE" elif ! is_collector; then ctslog=`findmsg "CTS: Stack:" | awk '{print $1}'` debug "Using CTS control file: $ctslog" USER_NODES=`grep CTS: $ctslog | grep -v debug: | grep " \* " | sed s:.*\\\*::g | sort -u | tr '\\n' ' '` HA_LOGFACILITY=`findmsg "CTS:.*Environment.SyslogFacility" | awk '{print $NF}'` NODES_SOURCE=user fi # the goods ANALYSIS_F=analysis.txt DESCRIPTION_F=description.txt HALOG_F=ha-log.txt BT_F=backtraces.txt SYSINFO_F=sysinfo.txt SYSSTATS_F=sysstats.txt DLM_DUMP_F=dlm_dump.txt TIME_F=time.txt export ANALYSIS_F DESCRIPTION_F HALOG_F BT_F SYSINFO_F SYSSTATS_F DLM_DUMP_F TIME_F CRM_MON_F=crm_mon.txt MEMBERSHIP_F=members.txt HB_UUID_F=hb_uuid.txt HOSTCACHE=hostcache CRM_VERIFY_F=crm_verify.txt PERMISSIONS_F=permissions.txt CIB_F=cib.xml CIB_TXT_F=cib.txt COROSYNC_RECORDER_F=fdata.txt export CRM_MON_F MEMBERSHIP_F CRM_VERIFY_F CIB_F CIB_TXT_F HB_UUID_F PERMISSIONS_F export COROSYNC_RECORDER_F CONFIGURATIONS="/etc/drbd.conf /etc/drbd.d /etc/booth/booth.conf" export CONFIGURATIONS THIS_IS_NODE="" if ! is_collector; then MASTER_NODE=$WE NODES=`getnodes` debug "nodes: `echo $NODES`" fi NODECNT=`echo $NODES | wc -w` if [ "$NODECNT" = 0 ]; then fatal "could not figure out a list of nodes; is this a cluster node?" fi if echo $NODES | grep -wqs $WE; then # are we a node? THIS_IS_NODE=1 fi # this only on master if ! is_collector; then # if this is not a node, then some things afterwards might # make no sense (not work) if ! is_node && [ "$NODES_SOURCE" != user ]; then warning "this is not a node and you didn't specify a list of nodes using -n" fi # # part 2: ssh business # # find out if ssh works if [ -z "$NO_SSH" ]; then # if the ssh user was supplied, consider that it # works; helps reduce the number of ssh invocations if [ -z "$SSH_USER" ]; then SSH_USER=`findsshuser` fi if [ -n "$SSH_USER" ]; then SSH_OPTS="$SSH_OPTS -o User=$SSH_USER" fi fi # assume that only root can collect data SUDO="" if [ -z "$SSH_USER" -a `id -u` != 0 ] || [ "$SSH_USER" != root ]; then debug "ssh user other than root, use sudo" SUDO="sudo -u root" fi LOCAL_SUDO="" if [ `id -u` != 0 ]; then debug "local user other than root, use sudo" LOCAL_SUDO="sudo -u root" fi fi if is_collector && [ "$HA_LOGFACILITY" ]; then logmark $HA_LOGFACILITY.$HA_LOGLEVEL $UNIQUE_MSG # allow for the log message to get (hopefully) written to the # log file sleep 1 fi # # part 4: find the logs and cut out the segment for the period # # if the master is also a node, getlog is going to be invoked # from the collector (is_master && is_node) || getlog if ! is_collector; then for node in $NODES; do if [ -z "$SSH_USER" ]; then start_slave_collector $node & SLAVEPIDS="$SLAVEPIDS $!" else start_slave_collector $node fi done fi # # part 5: endgame: # slaves tar their results to stdout, the master waits # for them, analyses results, asks the user to edit the # problem description template, and prints final notes # if is_collector; then collect_info (cd $WORKDIR/.. && tar -h -cf - $WE) else if [ -n "$SLAVEPIDS" ]; then wait $SLAVEPIDS fi analyze $WORKDIR > $WORKDIR/$ANALYSIS_F & events $WORKDIR & mktemplate > $WORKDIR/$DESCRIPTION_F [ "$NO_DESCRIPTION" ] || { echo press enter to edit the problem description... read junk edittemplate $WORKDIR/$DESCRIPTION_F } test -f $WORKDIR/$HALOG_F || combine_logs $WORKDIR wait if [ "$COMPRESS" = "1" ]; then pickcompress (cd $WORKDIR/.. && tar cf - $DEST) | $COMPRESS_PROG > $DESTDIR/$DEST.tar$COMPRESS_EXT else mv $WORKDIR $DESTDIR fi finalword fi Reusable-Cluster-Components-glue--3cff550e1084/hb_report/openais_conf_support.sh0000644000000000000000000000472612120057602030051 0ustar00usergroup00000000000000 # Copyright (C) 2007 Dejan Muhamedagic # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Stack specific part (openais) # openais.conf/logd.cf parsing # # cut out a stanza getstanza() { awk -v name="$1" ' !in_stanza && NF==2 && /^[a-z][a-z]*[[:space:]]*{/ { # stanza start if ($1 == name) in_stanza = 1 } in_stanza { print } in_stanza && NF==1 && $1 == "}" { exit } ' } # supply stanza in $1 and variable name in $2 # (stanza is optional) getcfvar() { [ -f "$CONF" ] || return sed 's/#.*//' < $CONF | if [ $# -eq 2 ]; then getstanza "$1" shift 1 else cat fi | awk -v varname="$1" ' NF==2 && match($1,varname":$")==1 { print $2; exit; } ' } iscfvarset() { test "`getcfvar $1`" } iscfvartrue() { getcfvar $1 $2 | egrep -qsi "^(true|y|yes|on|1)" } uselogd() { iscfvartrue use_logd } get_ais_logvars() { if iscfvartrue to_file; then HA_LOGFILE=`getcfvar logfile` HA_LOGFILE=${HA_LOGFILE:-"syslog"} HA_DEBUGFILE=$HA_LOGFILE elif iscfvartrue to_syslog; then HA_LOGFACILITY=`getcfvar syslog_facility` HA_LOGFACILITY=${HA_LOGFACILITY:-"daemon"} fi } getlogvars() { HA_LOGFACILITY=${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY} HA_LOGLEVEL="info" iscfvartrue debug && # prefer debug level if set HA_LOGLEVEL="debug" if uselogd; then [ -f "$LOGD_CF" ] || { debug "logd used but logd.cf not found: using defaults" return # no configuration: use defaults } debug "reading log settings from $LOGD_CF" get_logd_logvars else debug "reading log settings from $CONF" get_ais_logvars fi } cluster_info() { : echo "openais version: how?" if [ "$CONF" = /etc/corosync/corosync.conf ]; then /usr/sbin/corosync -v fi } essential_files() { cat< # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # figure out the cluster type, depending on the process list # and existence of configuration files # get_cluster_type() { if ps -ef | egrep -qs '[a]isexec|[c]orosync' || [ -f /etc/ais/openais.conf -a ! -f "$HA_CF" ] || [ -f /etc/corosync/corosync.conf -a ! -f "$HA_CF" ] then debug "this is OpenAIS cluster stack" echo "openais" else debug "this is Heartbeat cluster stack" echo "heartbeat" fi } # # find out which membership tool is installed # echo_membership_tool() { local f membership_tools membership_tools="ccm_tool crm_node" for f in $membership_tools; do which $f 2>/dev/null && break done } # find out if ptest or crm_simulate # echo_ptest_tool() { local f ptest_progs ptest_progs="crm_simulate ptest" for f in $ptest_progs; do which $f 2>/dev/null && break done } # # find nodes for this cluster # getnodes() { # 1. set by user? if [ "$USER_NODES" ]; then echo $USER_NODES # 2. running crm elif iscrmrunning; then debug "querying CRM for nodes" get_crm_nodes # 3. hostcache elif [ -f $HA_VARLIB/hostcache ]; then debug "reading nodes from $HA_VARLIB/hostcache" awk '{print $1}' $HA_VARLIB/hostcache # 4. ha.cf elif [ "$CLUSTER_TYPE" = heartbeat ]; then debug "reading nodes from ha.cf" getcfvar node # 5. if the cluster's stopped, try the CIB elif [ -f $CIB_DIR/$CIB_F ]; then debug "reading nodes from the archived $CIB_DIR/$CIB_F" (CIB_file=$CIB_DIR/$CIB_F get_crm_nodes) fi } logd_getcfvar() { sed 's/#.*//' < $LOGD_CF | grep -w "^$1" | sed 's/^[^[:space:]]*[[:space:]]*//' } get_logd_logvars() { # unless logfacility is set to none, heartbeat/ha_logd are # going to log through syslog HA_LOGFACILITY=`logd_getcfvar logfacility` [ "" = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=$DEFAULT_HA_LOGFACILITY [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" HA_LOGFILE=`logd_getcfvar logfile` HA_DEBUGFILE=`logd_getcfvar debugfile` } findlogdcf() { local f for f in \ `test -x $HA_BIN/ha_logd && which strings > /dev/null 2>&1 && strings $HA_BIN/ha_logd | grep 'logd\.cf'` \ `for d; do echo $d/logd.cf $d/ha_logd.cf; done` do if [ -f "$f" ]; then echo $f debug "found logd.cf at $f" return 0 fi done debug "no logd.cf" return 1 } # # logging # syslogmsg() { local severity logtag severity=$1 shift 1 logtag="" [ "$HA_LOGTAG" ] && logtag="-t $HA_LOGTAG" logger -p ${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}.$severity $logtag $* } # # find log destination # findmsg() { local d syslogdirs favourites mark log # this is tricky, we try a few directories syslogdirs="/var/log /var/logs /var/syslog /var/adm /var/log/ha /var/log/cluster /var/log/pacemaker /var/log/heartbeat /var/log/crm /var/log/corosync /var/log/openais" favourites="ha-*" mark=$1 log="" for d in $syslogdirs; do [ -d $d ] || continue log=`grep -l -e "$mark" $d/$favourites` && break test "$log" && break log=`grep -l -e "$mark" $d/*` && break test "$log" && break done 2>/dev/null [ "$log" ] && ls -t $log | tr '\n' ' ' [ "$log" ] && debug "found HA log at `ls -t $log | tr '\n' ' '`" || debug "no HA log found in $syslogdirs" } # # print a segment of a log file # str2time() { perl -e "\$time='$*';" -e ' eval "use Date::Parse"; if (!$@) { print str2time($time); } else { eval "use Date::Manip"; if (!$@) { print UnixDate(ParseDateString($time), "%s"); } } ' } getstamp_syslog() { awk '{print $1,$2,$3}' } getstamp_legacy() { awk '{print $2}' | sed 's/_/ /' } linetime() { local l l=`tail -n +$2 $1 | head -1 | $getstampproc` str2time "$l" } find_getstampproc() { local t l func trycnt t=0 l="" func="" trycnt=10 while [ $trycnt -gt 0 ] && read l; do t=$(str2time `echo $l | getstamp_syslog`) if [ "$t" ]; then func="getstamp_syslog" debug "the log file is in the syslog format" break fi t=$(str2time `echo $l | getstamp_legacy`) if [ "$t" ]; then func="getstamp_legacy" debug "the log file is in the legacy format (please consider switching to syslog format)" break fi trycnt=$(($trycnt-1)) done echo $func } find_first_ts() { local l ts while read l; do ts=$(str2time "`echo $l | $getstampproc`") [ "$ts" ] && break warning "cannot extract time: |$l|; will try the next one" done echo $ts } findln_by_time() { local logf=$1 local tm=$2 local first=1 local last=`wc -l < $logf` local tmid mid trycnt while [ $first -le $last ]; do mid=$((($last+$first)/2)) trycnt=10 while [ $trycnt -gt 0 ]; do tmid=`linetime $logf $mid` [ "$tmid" ] && break warning "cannot extract time: $logf:$mid; will try the next one" trycnt=$(($trycnt-1)) # shift the whole first-last segment first=$(($first-1)) last=$(($last-1)) mid=$((($last+$first)/2)) done if [ -z "$tmid" ]; then warning "giving up on log..." return fi if [ $tmid -gt $tm ]; then last=$(($mid-1)) elif [ $tmid -lt $tm ]; then first=$(($mid+1)) else break fi done echo $mid } dumplog() { local logf=$1 local from_line=$2 local to_line=$3 [ "$from_line" ] || return tail -n +$from_line $logf | if [ "$to_line" ]; then head -$(($to_line-$from_line+1)) else cat fi } # # find files newer than a and older than b # isnumber() { echo "$*" | grep -qs '^[0-9][0-9]*$' } touchfile() { local t t=`mktemp` && perl -e "\$file=\"$t\"; \$tm=$1;" -e 'utime $tm, $tm, $file;' && echo $t } find_files() { local dirs from_time to_time local from_stamp to_stamp findexp dirs=$1 from_time=$2 to_time=$3 isnumber "$from_time" && [ "$from_time" -gt 0 ] || { warning "sorry, can't find files based on time if you don't supply time" return } if ! from_stamp=`touchfile $from_time`; then warning "can't create temporary files" return fi add_tmpfiles $from_stamp findexp="-newer $from_stamp" if isnumber "$to_time" && [ "$to_time" -gt 0 ]; then if ! to_stamp=`touchfile $to_time`; then warning "can't create temporary files" return fi add_tmpfiles $to_stamp findexp="$findexp ! -newer $to_stamp" fi find $dirs -type f $findexp } # # check permissions of files/dirs # pl_checkperms() { perl -e ' # check permissions and ownership # uid and gid are numeric # everything must match exactly # no error checking! (file should exist, etc) ($filename, $perms, $in_uid, $in_gid) = @ARGV; ($mode,$uid,$gid) = (stat($filename))[2,4,5]; $p=sprintf("%04o", $mode & 07777); $p ne $perms and exit(1); $uid ne $in_uid and exit(1); $gid ne $in_gid and exit(1); ' $* } num_id() { getent $1 $2 | awk -F: '{print $3}' } chk_id() { [ "$2" ] && return 0 echo "$1: id not found" return 1 } check_perms() { local f p uid gid n_uid n_gid essential_files | while read type f p uid gid; do [ -$type $f ] || { echo "$f wrong type or doesn't exist" continue } n_uid=`num_id passwd $uid` chk_id "$uid" "$n_uid" || continue n_gid=`num_id group $gid` chk_id "$gid" "$n_gid" || continue pl_checkperms $f $p $n_uid $n_gid || { echo "wrong permissions or ownership for $f:" ls -ld $f } done } # # coredumps # pkg_mgr_list() { # list of: # regex pkg_mgr # no spaces allowed in regex cat<&1 | awk -v bins="$MYBINARIES" ' n>0 && /^Try: zypper install/ {gsub("\"",""); print $NF} n>0 {n=0} /Missing separate debuginfo/ && match($NF, bins) {n=1} ' | sort -u } fetchpkg_zypper() { debug "get debuginfo packages using zypper: $@" zypper -qn ref > /dev/null zypper -qn install -C $@ >/dev/null } find_pkgmgr() { local binary=$1 core=$2 local regex pkg_mgr pkg_mgr_list | while read regex pkg_mgr; do if gdb $binary $core &1 | grep "$regex" > /dev/null; then echo $pkg_mgr break fi done } get_debuginfo() { local binary=$1 core=$2 local pkg_mgr pkgs gdb $binary $core /dev/null | grep 'no debugging symbols found' > /dev/null || return # no missing debuginfo pkg_mgr=`find_pkgmgr $binary $core` if [ -z "$pkg_mgr" ]; then warning "found core for $binary but there is no debuginfo and we don't know how to get it on this platform" return fi pkgs=`listpkg_$pkg_mgr $binary $core` [ -n "$pkgs" ] && fetchpkg_$pkg_mgr $pkgs } findbinary() { local random_binary binary fullpath random_binary=`which cat 2>/dev/null` # suppose we are lucky binary=`gdb $random_binary $1 < /dev/null 2>/dev/null | grep 'Core was generated' | awk '{print $5}' | sed "s/^.//;s/[.':]*$//"` if [ x = x"$binary" ]; then debug "could not detect the program name for core $1 from the gdb output; will try with file(1)" binary=$(file $1 | awk '/from/{ for( i=1; i<=NF; i++ ) if( $i == "from" ) { print $(i+1) break } }') binary=`echo $binary | tr -d "'"` binary=$(echo $binary | tr -d '`') if [ "$binary" ]; then binary=`which $binary 2>/dev/null` fi fi if [ x = x"$binary" ]; then warning "could not find the program path for core $1" return fi fullpath=`which $binary 2>/dev/null` if [ x = x"$fullpath" ]; then for d in $HA_BIN $CRM_DAEMON_DIR; do if [ -x $d/$binary ]; then echo $d/$binary debug "found the program at $d/$binary for core $1" else warning "could not find the program path for core $1" fi done else echo $fullpath debug "found the program at $fullpath for core $1" fi } getbt() { local corefile absbinpath which gdb > /dev/null 2>&1 || { warning "please install gdb to get backtraces" return } for corefile; do absbinpath=`findbinary $corefile` [ x = x"$absbinpath" ] && continue get_debuginfo $absbinpath $corefile echo "====================== start backtrace ======================" ls -l $corefile gdb -batch -n -quiet -ex ${BT_OPTS:-"thread apply all bt full"} -ex quit \ $absbinpath $corefile 2>/dev/null echo "======================= end backtrace =======================" done } # # heartbeat configuration/status # iscrmrunning() { local pid maxwait ps -ef | grep -qs [c]rmd || return 1 crmadmin -D >/dev/null 2>&1 & pid=$! maxwait=100 while kill -0 $pid 2>/dev/null && [ $maxwait -gt 0 ]; do sleep 0.1 maxwait=$(($maxwait-1)) done if kill -0 $pid 2>/dev/null; then kill $pid false else wait $pid fi } dumpstate() { crm_mon -1 | grep -v '^Last upd' > $1/$CRM_MON_F cibadmin -Ql > $1/$CIB_F `echo_membership_tool` $MEMBERSHIP_TOOL_OPTS -p > $1/$MEMBERSHIP_F 2>&1 } getconfig() { [ -f "$CONF" ] && cp -p $CONF $1/ [ -f "$LOGD_CF" ] && cp -p $LOGD_CF $1/ if iscrmrunning; then dumpstate $1 touch $1/RUNNING else cp -p $CIB_DIR/$CIB_F $1/ 2>/dev/null touch $1/STOPPED fi [ "$HOSTCACHE" ] && cp -p $HA_VARLIB/hostcache $1/$HOSTCACHE 2>/dev/null [ "$HB_UUID_F" ] && crm_uuid -r > $1/$HB_UUID_F 2>&1 [ -f "$1/$CIB_F" ] && crm_verify -V -x $1/$CIB_F >$1/$CRM_VERIFY_F 2>&1 } crmconfig() { [ -f "$1/$CIB_F" ] && which crm >/dev/null 2>&1 && CIB_file=$1/$CIB_F crm configure show >$1/$CIB_TXT_F 2>&1 } get_crm_nodes() { cibadmin -Ql -o nodes | awk ' //dev/null 2>&1; then fping -a $@ 2>/dev/null else local h for h; do ping -c 2 -q $h >/dev/null 2>&1 && echo $h; done fi } # # remove values of sensitive attributes # # this is not proper xml parsing, but it will work under the # circumstances is_sensitive_xml() { local patt epatt epatt="" for patt in $SANITIZE; do epatt="$epatt|$patt" done epatt="`echo $epatt|sed 's/.//'`" egrep -qs "name=\"$epatt\"" } test_sensitive_one() { local file compress decompress file=$1 compress="" echo $file | grep -qs 'gz$' && compress=gzip echo $file | grep -qs 'bz2$' && compress=bzip2 if [ "$compress" ]; then decompress="$compress -dc" else compress=cat decompress=cat fi $decompress < $file | is_sensitive_xml } sanitize_xml_attrs() { local patt sed $( for patt in $SANITIZE; do echo "-e /name=\"$patt\"/s/value=\"[^\"]*\"/value=\"****\"/" done ) } sanitize_hacf() { awk ' $1=="stonith_host"{ for( i=5; i<=NF; i++ ) $i="****"; } {print} ' } sanitize_one() { local file compress decompress tmp ref file=$1 compress="" echo $file | grep -qs 'gz$' && compress=gzip echo $file | grep -qs 'bz2$' && compress=bzip2 if [ "$compress" ]; then decompress="$compress -dc" else compress=cat decompress=cat fi tmp=`mktemp` ref=`mktemp` add_tmpfiles $tmp $ref if [ -z "$tmp" -o -z "$ref" ]; then fatal "cannot create temporary files" fi touch -r $file $ref # save the mtime if [ "`basename $file`" = ha.cf ]; then sanitize_hacf else $decompress | sanitize_xml_attrs | $compress fi < $file > $tmp mv $tmp $file touch -r $ref $file } # # keep the user posted # fatal() { echo "`uname -n`: ERROR: $*" >&2 exit 1 } warning() { echo "`uname -n`: WARN: $*" >&2 } info() { echo "`uname -n`: INFO: $*" >&2 } debug() { [ "$VERBOSITY" ] && [ $VERBOSITY -gt 0 ] && echo "`uname -n`: DEBUG: $*" >&2 return 0 } pickfirst() { for x; do which $x >/dev/null 2>&1 && { echo $x return 0 } done return 1 } # tmp files business drop_tmpfiles() { trap 'rm -rf `cat $__TMPFLIST`; rm $__TMPFLIST' EXIT } init_tmpfiles() { if __TMPFLIST=`mktemp`; then drop_tmpfiles else # this is really bad, let's just leave fatal "eek, mktemp cannot create temporary files" fi } add_tmpfiles() { test -f "$__TMPFLIST" || return echo $* >> $__TMPFLIST } # # get some system info # distro() { local relf f which lsb_release >/dev/null 2>&1 && { lsb_release -d debug "using lsb_release for distribution info" return } relf=`ls /etc/debian_version 2>/dev/null` || relf=`ls /etc/slackware-version 2>/dev/null` || relf=`ls -d /etc/*-release 2>/dev/null` && { for f in $relf; do test -f $f && { echo "`ls $f` `cat $f`" debug "found $relf distribution release file" return } done } warning "no lsb_release, no /etc/*-release, no /etc/debian_version: no distro information" } pkg_ver_deb() { dpkg-query -f '${Name} ${Version}' -W $* 2>/dev/null debsums -s $* 2>/dev/null } pkg_ver_rpm() { { rpm -q --qf '%{name} %{version}-%{release} - %{distribution} %{arch}\n' $* rpm --verify $* } 2>&1 | grep -v 'not installed' } pkg_ver_pkg_info() { for pkg; do pkg_info | grep $pkg done } pkg_ver_pkginfo() { for pkg; do pkginfo $pkg | awk '{print $3}' # format? done } pkg_ver() { local pkg_mgr if which dpkg >/dev/null 2>&1 ; then pkg_mgr="deb" elif which rpm >/dev/null 2>&1 ; then pkg_mgr="rpm" elif which pkg_info >/dev/null 2>&1 ; then pkg_mgr="pkg_info" elif which pkginfo >/dev/null 2>&1 ; then pkg_mgr="pkginfo" else echo "Unknown package manager!" return fi debug "the package manager is $pkg_mgr" pkg_ver_$pkg_mgr $* } crm_info() { $CRM_DAEMON_DIR/crmd version 2>&1 } Reusable-Cluster-Components-glue--3cff550e1084/include/Makefile.am0000644000000000000000000000167412120057602024750 0ustar00usergroup00000000000000# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = clplumbing pils stonith lrm idir=$(includedir)/heartbeat i_HEADERS = compress.h glue_config.h ha_msg.h noinst_HEADERS = config.h lha_internal.h replace_uuid.h Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/GSource.h0000644000000000000000000001736112120057602026570 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_GSOURCE_H # define _CLPLUMBING_GSOURCE_H # include typedef struct GFDSource_s GFDSource; typedef struct GCHSource_s GCHSource; typedef struct GWCSource_s GWCSource; typedef struct GSIGSource_s GSIGSource; typedef struct GTRIGSource_s GTRIGSource; void G_main_setmaxdispatchdelay(GSource* s, unsigned long delayms); void G_main_setmaxdispatchtime(GSource* s, unsigned long dispatchms); void G_main_setdescription(GSource* s, const char * description); void G_main_setmaxdispatchdelay_id(guint id, unsigned long delayms); void G_main_setmaxdispatchtime_id(guint id, unsigned long dispatchms); void G_main_setdescription_id(guint id, const char * description); void G_main_setall_id(guint id, const char * description, unsigned long delayms, unsigned long dispatchms); /*********************************************************************** * Functions for interfacing input to the mainloop ***********************************************************************/ GSource* G_main_add_input(int priority, gboolean can_recurse, GSourceFuncs* funcs); /*********************************************************************** * Functions for interfacing "raw" file descriptors to the mainloop ***********************************************************************/ /* * Add a file descriptor to the gmainloop world... */ GFDSource* G_main_add_fd(int priority, int fd, gboolean can_recurse , gboolean (*dispatch)(int fd, gpointer user_data) , gpointer userdata , GDestroyNotify notify); /* * Delete a file descriptor from the gmainloop world... * Note: destroys the GFDSource object. */ gboolean G_main_del_fd(GFDSource* fdp); /* * Notify us that a file descriptor is blocked on output. * (i.e., we should poll for output events) */ void g_main_output_is_blocked(GFDSource* fdp); /************************************************************** * Functions for interfacing IPC_Channels to the mainloop **************************************************************/ /* * Add an IPC_channel to the gmainloop world... */ GCHSource* G_main_add_IPC_Channel(int priority, IPC_Channel* ch , gboolean can_recurse , gboolean (*dispatch)(IPC_Channel* source_data , gpointer user_data) , gpointer userdata , GDestroyNotify notify); /* * the events in this source is paused/resumed */ void G_main_IPC_Channel_pause(GCHSource* chp); void G_main_IPC_Channel_resume(GCHSource* chp); /* * Delete an IPC_channel from the gmainloop world... * Note: destroys the GCHSource object, and the IPC_Channel * object automatically. */ gboolean G_main_del_IPC_Channel(GCHSource* chp); /* * Set the destroy notify function * */ void set_IPC_Channel_dnotify(GCHSource* chp, GDestroyNotify notify); /********************************************************************* * Functions for interfacing IPC_WaitConnections to the mainloop ********************************************************************/ /* * Add an IPC_WaitConnection to the gmainloop world... * Note that the dispatch function is called *after* the * connection is accepted. */ GWCSource* G_main_add_IPC_WaitConnection(int priority, IPC_WaitConnection* ch , IPC_Auth* auth_info , gboolean can_recurse , gboolean (*dispatch)(IPC_Channel* source_data , gpointer user_data) , gpointer userdata , GDestroyNotify notify); /* * Delete an IPC_WaitConnection from the gmainloop world... * Note: destroys the GWCSource object, and the IPC_WaitConnection * object automatically. */ gboolean G_main_del_IPC_WaitConnection(GWCSource* wcp); /************************************************************** * Functions for interfacing Signals to the mainloop **************************************************************/ /* * Add an Signal to the gmainloop world... */ GSIGSource* G_main_add_SignalHandler( int priority, int signal, gboolean (*dispatch)(int nsig, gpointer user_data), gpointer userdata, GDestroyNotify notify); /* * Delete an signal from the gmainloop world... * Note: destroys the GSIGSource object, and the removes the * Signal Handler automatically. */ gboolean G_main_del_SignalHandler(GSIGSource* chp); /* * Set the destroy notify function * */ void set_SignalHandler_dnotify(GSIGSource* chp, GDestroyNotify notify); /* manage child process death using sig source*/ #define DEFAULT_MAXDISPATCHTIME 30 /* in ms */ void set_sigchld_proctrack(int priority, unsigned long maxdisptime); /************************************************************** * Functions for interfacing Manual triggers to the mainloop **************************************************************/ /* * Add an Trigger to the gmainloop world... */ GTRIGSource* G_main_add_TriggerHandler( int priority, gboolean (*dispatch)(gpointer user_data), gpointer userdata, GDestroyNotify notify); /* * Delete an signal from the gmainloop world... * Note: destroys the GTRIGSource object, and the removes the * Trigger Handler automatically. */ gboolean G_main_del_TriggerHandler(GTRIGSource* chp); /* * Set the destroy notify function * */ void set_TriggerHandler_dnotify(GTRIGSource* chp, GDestroyNotify notify); void G_main_set_trigger(GTRIGSource* man_src); /* * Create a trigger for triggering an action in a short-lived (temporary) * child process. * * The name isn't wonderful, but we couldn't think of a better one. */ GTRIGSource* G_main_add_tempproc_trigger(int priority , int (*fun)(gpointer p) /* What to do? */ /* Called in child process */ , const char * procname /* What do we call this process? */ , gpointer userdata /* Passed to 'triggerfun' */ , void (*prefork)(gpointer p) /* Called before fork */ , void (*postfork)(gpointer p) /* Called by parent process * after fork(2) call. * Each has 'userdata' * passed to it. */ , void (*complete)(gpointer p, int status, int signo, int exitcode)); /* called after the child process completes */ /* * Special notes: * - No more than one child process will be active at a time per trigger * object. * * - If you trigger the action while this object has a child active, * then it will be re-triggered after the currently running child * completes. There is no necessary correlation between the * number of times a the action is triggered and how many * times it is executed. What is guaranteed is that after you * trigger the action, it will happen (at least) once - as soon * as the scheduler gets around to it at the priority you've * assigned it. But if several are triggered while a child * process is running, only one process will be instantiated to * take the action requested by all the trigger calls. * * - Child processes are forked off at the priority of the trigger, * not the priority of the SIGCHLD handler. * * - This is useful for writing out updates to a file for example. * While we're writing one copy out, subsequent updates are * held off until this one completes. When it completes, then * the file is written again - but not "n" times - just the * latest version available at the time the trigger is * activated (run). */ #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/GSource_internal.h0000644000000000000000000000717012120057602030461 0ustar00usergroup00000000000000/* * Author: Alan Robertson * Copyright (C) 2005 International Business Machines Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #define MAG_GFDSOURCE 0xfeed0001U #define MAG_GCHSOURCE 0xfeed0002U #define MAG_GWCSOURCE 0xfeed0003U #define MAG_GSIGSOURCE 0xfeed0004U #define MAG_GTRIGSOURCE 0xfeed0005U #define MAG_GTIMEOUTSRC 0xfeed0006U #define IS_FDSOURCE(p) (p && (p)->magno == MAG_GFDSOURCE) #define IS_CHSOURCE(p) (p && (p)->magno == MAG_GCHSOURCE) #define IS_WCSOURCE(p) (p && (p)->magno == MAG_GWCSOURCE) #define IS_SIGSOURCE(p) (p && (p)->magno == MAG_GSIGSOURCE) #define IS_TRIGSOURCE(p) (p && (p)->magno == MAG_GTRIGSOURCE) #define IS_TIMEOUTSRC(p) (p && (p)->magno == MAG_GTIMEOUTSRC) #define IS_ONEOFOURS(p) (IS_CHSOURCE(p)|IS_FDSOURCE(p)|IS_WCSOURCE(p)|| \ IS_SIGSOURCE(p)|IS_TRIGSOURCE(p)||IS_TIMEOUTSRC(p)) #define DEFAULT_MAXDISPATCH 0 #define DEFAULT_MAXDELAY 0 #define OTHER_MAXDELAY 100 #define COMMON_STRUCTSTART \ GSource source; /* Common glib struct - must be 1st */ \ unsigned magno; /* Magic number */ \ long maxdispatchms; /* Time limit for dispatch function */ \ long maxdispatchdelayms; /* Max delay before processing */ \ char detecttime[sizeof(longclock_t)]; \ /* Time last input detected */ \ void* udata; /* User-defined data */ \ guint gsourceid; /* Source id of this source */ \ const char * description; /* Description of this source */ \ GDestroyNotify dnotify struct GFDSource_s { COMMON_STRUCTSTART; gboolean (*dispatch)(int fd, gpointer user_data); GPollFD gpfd; }; typedef gboolean (*GCHdispatch)(IPC_Channel* ch, gpointer user_data); struct GCHSource_s { COMMON_STRUCTSTART; IPC_Channel* ch; gboolean fd_fdx; GPollFD infd; GPollFD outfd; gboolean dontread; /* TRUE when we don't want to read * more input for a while - we're * flow controlling the writer off */ gboolean (*dispatch)(IPC_Channel* ch, gpointer user_data); }; struct GWCSource_s { COMMON_STRUCTSTART; GPollFD gpfd; IPC_WaitConnection* wch; IPC_Auth* auth_info; gboolean (*dispatch)(IPC_Channel* accept_ch, gpointer udata); }; struct GSIGSource_s { COMMON_STRUCTSTART; clock_t sh_detecttime; int signal; gboolean signal_triggered; gboolean (*dispatch)(int signal, gpointer user_data); }; struct GTRIGSource_s { COMMON_STRUCTSTART; gboolean manual_trigger; gboolean (*dispatch)(gpointer user_data); }; /************************************************************ * Functions for IPC_Channels ***********************************************************/ gboolean G_CH_prepare_int(GSource* source, gint* timeout); gboolean G_CH_check_int(GSource* source); gboolean G_CH_dispatch_int(GSource* source, GSourceFunc callback, gpointer user_data); void G_CH_destroy_int(GSource* source); GCHSource* G_main_IPC_Channel_constructor(GSource* source, IPC_Channel* ch , gpointer userdata, GDestroyNotify notify); Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/Gmain_timeout.h0000644000000000000000000000277112120057602030021 0ustar00usergroup00000000000000#ifndef _CLPLUMBING_GMAIN_TIMEOUT_H #define _CLPLUMBING_GMAIN_TIMEOUT_H #include /* * Copyright (C) 2002 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * These functions must work correctly even if someone resets the * time-of-day clock. The g_main_timeout_add() function does not have * this property, since it relies on gettimeofday(). * * Our functions have the same semantics - except they always work ;-) * * This is because we use longclock_t for our time values. */ guint Gmain_timeout_add(guint interval , GSourceFunc function , gpointer data); guint Gmain_timeout_add_full(gint priority , guint interval , GSourceFunc function , gpointer data , GDestroyNotify notify); void Gmain_timeout_remove(guint tag); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/Makefile.am0000644000000000000000000000261112120057602027074 0ustar00usergroup00000000000000# # linux-ha: Linux-HA heartbeat code # # Copyright (C) 2002 International Business Machines. # Author: Alan Robertson # # 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 St, Fifth Floor, Boston, MA 02110-1301 USA # MAINTAINERCLEANFILES = Makefile.in idir=$(includedir)/clplumbing i_HEADERS = \ Gmain_timeout.h \ GSource.h \ GSource_internal.h \ apphb_cs.h \ base64.h \ cl_log.h \ cl_poll.h \ cl_signal.h \ cl_pidfile.h \ cl_random.h \ cl_reboot.h \ cl_syslog.h \ cl_uuid.h \ coredumps.h \ cpulimits.h \ ipc.h \ lsb_exitcodes.h \ loggingdaemon.h \ longclock.h \ mkstemp_mode.h \ netstring.h \ proctrack.h \ realtime.h \ replytrack.h \ setproctitle.h \ timers.h \ uids.h \ cl_misc.h \ md5.h \ cl_plugin.h \ cl_tiebreaker.h \ cl_quorum.h \ cl_quorumd.h Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/apphb_cs.h0000644000000000000000000000400212120057602026764 0ustar00usergroup00000000000000/* * Copyright (C) 2002 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _CLPLUMBING_APPHB_CS_H #define _CLPLUMBING_APPHB_CS_H /* Internal client-server messages for APP heartbeat service */ #ifndef HA_VARRUNDIR #define HA_VARRUNDIR "/var/run" #endif #define APPHBSOCKPATH HA_VARRUNDIR "/heartbeat/apphb.comm" #define APPHB_TLEN 8 #define APPHB_OLEN 256 #define REGISTER "reg" #define UNREGISTER "unreg" #define HEARTBEAT "hb" #define SETINTERVAL "setint" #define SETWARNTIME "setwarn" #define SETREBOOT "setboot" /* * These messages are really primitive. * They don't have any form of version control, they're in host byte order, * and they're all in binary... * * Fortunately, this is a very simple local service ;-) */ /* Generic (no parameter) App heartbeat message */ struct apphb_msg { char msgtype [APPHB_TLEN]; }; /* App heartbeat Registration message */ struct apphb_signupmsg { char msgtype [APPHB_TLEN]; char appname [APPHB_OLEN]; char appinstance [APPHB_OLEN]; char curdir [APPHB_OLEN]; pid_t pid; uid_t uid; gid_t gid; }; /* App heartbeat setinterval / setwarn message */ struct apphb_msmsg { char msgtype [APPHB_TLEN]; unsigned long ms; }; /* App heartbeat server return code (errno) */ struct apphb_rc { int rc; }; #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/base64.h0000644000000000000000000000377312120057602026307 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_BASE64_H # define _CLPLUMBING_BASE64_H /* * * Base64 conversion functions. * They convert from a binary array into a single string * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341 * The only difference is that we don't care about line lengths. * We do use their encoding algorithm. * */ #define B64inunit 3 #define B64outunit 4 /* How long will the base64 string be for a particular binary object size? */ /* This is like strlen() and doesn't include the '\0' byte at the end */ #define B64_stringlen(bytes) \ ((((bytes)+(B64inunit-1))/B64inunit)*B64outunit) /* How many bytes to you need to malloc to store a base64 string? */ /* (includes space for the '\0' terminator byte) */ #define B64_stringspace(bytes) (B64_stringlen(bytes)+1) /* How many bytes will a base64 string take up back in binary? */ /* Note: This may be as much as two 2 bytes more than strictly needed */ #define B64_maxbytelen(slen) (((slen) / B64outunit)*B64inunit) /* Returns strlen() of base64 string returned in "output" */ int binary_to_base64(const void * data, int nbytes, char * output, int outlen); /* Returns the size of the binary object we returned in "output" */ int base64_to_binary(const char * input, int inlen, void * output, int outlen); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_log.h0000644000000000000000000000754212120057602026460 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_CL_LOG_H # define _CLPLUMBING_CL_LOG_H # include # include #define TIME_T unsigned long #define HA_FAIL 0 #define HA_OK 1 #define MAXLINE (512*10) /* this is defined by the caller */ struct logspam { const char *id; /* identifier */ int max; /* maximum number of messages ... */ time_t window; /* ... within this timeframe */ time_t reset_time; /* log new messages after this time */ const char *advice; /* what to log in case messages get suppressed */ }; /* this is internal (oblique to the caller) */ struct msg_ctrl { struct logspam *lspam; /* */ time_t *msg_slots; /* msg slot root (space for lspam->max) */ int last; /* last used msg slot [0..lspam->max-1]; -1 on init */ int cnt; /* current msg count [0..lspam->max] */ time_t suppress_t; /* messages blocked since this time */ }; struct IPC_CHANNEL; extern int debug_level; #define ANYDEBUG (debug_level) #define DEBUGDETAILS (debug_level >= 2) #define DEBUGAUTH (debug_level >=3) #define DEBUGMODULE (debug_level >=3) #define DEBUGPKT (debug_level >= 4) #define DEBUGPKTCONT (debug_level >= 5) void cl_direct_log(int priority, const char* buf, gboolean, const char*, int, TIME_T); void cl_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3); void cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...) G_GNUC_PRINTF(3,4); struct msg_ctrl *cl_limit_log_new(struct logspam *lspam); void cl_limit_log_destroy(struct msg_ctrl *ml); void cl_limit_log_reset(struct msg_ctrl *ml); void cl_perror(const char * fmt, ...) G_GNUC_PRINTF(1,2); void cl_log_enable_stderr(int truefalse); void cl_log_enable_stdout(int truefalse); gboolean cl_log_test_logd(void); void cl_log_set_uselogd(int truefalse); void cl_log_enable_syslog_filefmt(int truefalse); void cl_log_use_buffered_io(int truefalse); gboolean cl_log_get_uselogd(void); void cl_log_set_facility(int facility); void cl_log_set_entity(const char * entity); void cl_log_set_syslogprefix(const char *prefix); void cl_log_set_logfile(const char * path); void cl_log_set_debugfile(const char * path); void cl_inherit_logging_environment(int maxqlen); int cl_log_set_logd_channel_source( void (*create_callback)(struct IPC_CHANNEL* chan), GDestroyNotify destroy_callback); int cl_log_get_logdtime(void); void cl_log_set_logdtime(int logdintval); char * ha_timestamp(TIME_T t); void cl_glib_msg_handler(const gchar *log_domain , GLogLevelFlags log_level, const gchar *message , gpointer user_data); void cl_flush_logs(void); void cl_log_args(int argc, char **argv); int cl_log_is_logd_fd(int fd); const char * prio2str(int priority); /* cl_log_use_buffered_io and cl_log_do_fflush as optimization for logd, * so it may buffer a few message lines, then fflush them out in one write. * Set do_fsync != 0, if you even want it to fsync. */ void cl_log_do_fflush(int do_fsync); void cl_log_use_buffered_io(int truefalse); /* We now keep the file handles open for a potentially very long time. * Sometimes we may need to close them explicitly. */ void cl_log_close_log_files(void); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_misc.h0000644000000000000000000000213212120057602026620 0ustar00usergroup00000000000000/* * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_CL_MISC_H #define _CLPLUMBING_CL_MISC_H int cl_str_to_boolean(const char*, int*); int cl_file_exists(const char* filename); char* cl_get_env(const char* env_name); int cl_binary_to_int(const char* data, int len); long cl_get_msec(const char * input); /* string to msec */ #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_pidfile.h0000644000000000000000000000173012120057602027304 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LOCKFILE_H_ #define _LOCKFILE_H_ int cl_read_pidfile(const char *filename); int cl_read_pidfile_no_checking(const char *filename); int cl_lock_pidfile(const char *filename); int cl_unlock_pidfile(const char *filename); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_plugin.h0000644000000000000000000000204312120057602027164 0ustar00usergroup00000000000000 /* * cl_manage_plugin.c: This file handle plugin loading and deleting * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CL_PLUGIN__ #define __CL_PLUGIN__ int cl_remove_plugin(const char* type, const char* pluginname); void* cl_load_plugin(const char* type, const char* pluginname); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_poll.h0000644000000000000000000000332512120057602026640 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CLPLUMBING_CL_POLL_H # define CLPLUMBING_CL_POLL_H #include #include /* * Poll the file descriptors described by the NFDS structures starting at * FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for * an event to occur; if TIMEOUT is -1, block until an event occurs. * Returns the number of file descriptors with events, zero if timed out, * or -1 for errors. * * When available, this function uses POSIX signals, and Linux F_SETSIG() * calls to provide this capability. When it is not available it * uses the real poll() call. * */ int cl_poll(struct pollfd *fds, unsigned int nfds, int timeout_ms); /* * Call cl_poll_ignore() when you close a file descriptor you monitored * via cl_poll() before, or if you don't want it monitored any more. */ int cl_poll_ignore(int fd); /* Select the signal you want us to use (must be a RT signal) */ int cl_poll_setsig(int nsig); int cl_glibpoll(GPollFD* ufds, guint nfsd, gint timeout); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_quorum.h0000644000000000000000000000260212120057602027217 0ustar00usergroup00000000000000/* * quorum.h: head file for quorum module * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _QUORUM_H_ #define _QUORUM_H_ #define HB_QUORUM_TYPE quorum #define HB_QUORUM_TYPE_S "quorum" #define QUORUM_YES 0 #define QUORUM_NO 1 #define QUORUM_TIE 2 typedef void(*callback_t)(void); /* * List of functions provided by implementations of the quorum interface. */ struct hb_quorum_fns { int (*getquorum) (const char* cluster , int member_count, int member_quorum_votes , int total_node_count, int total_quorum_votes); int (*init) (callback_t notify, const char* cluster, const char* quorum_server); void (*stop) (void); }; #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_quorumd.h0000644000000000000000000000304512120057602027365 0ustar00usergroup00000000000000/* * quorum.h: head file for quorum module * * Author: Huang Zhen * Copyright (C) 2006 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _QUORUMD_H_ #define _QUORUMD_H_ #define HB_QUORUMD_TYPE quorumd #define HB_QUORUMD_TYPE_S "quorumd" #define CONFIGFILE HA_HBCONF_DIR"/quorumd.conf" #define MAX_DN_LEN 256 #define quorum_log(priority, fmt...); \ cl_log(priority, fmt); \ #define quorum_debug(priority, fmt...); \ if ( debug_level > 0 ) { \ cl_log(priority, fmt); \ } /* List of functions provided by implementations of the quorumd interface. */ struct hb_quorumd_fns { int (*test) (void); int (*init) (void); int (*load_config_file) (void); int (*dump_data) (int priority); int (*on_connect) (int sock, gnutls_session session, const char* CN); }; #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_random.h0000644000000000000000000000627712120057602027163 0ustar00usergroup00000000000000 /* * Copyright (C) 2005 Guochun Shi * Copyright (C) 2005 International Business Machines Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include /* Intended usage is srand(cl_randseed()). * This returns on "as good as it gets" random number usually taken from * /dev/urandom to have a nice seed for future random numbers generated by * rand(). */ unsigned int cl_randseed(void); /* get_next_random() currently rand() based. * * You probably want to use cl_rand_from_interval instead. * * You don't need to srand(), it will seed once with cl_randseed internally. * * It is called that way, because it was exposed in the header file for a long * time, and used to be coded in an attempt to pregenerate a queue of random * numbers from the mainloop, and it would shift the next random number from * that queue and trigger generation of new random numbers "at idle time" to * refill that queue. * Only that functionality never actually worked, is not interessting anymore * anyways (rand() is cheap enough), and is now ripped out. * * So it now does srand(cl_randseed()) once internally, * and from there on is equivalent to calling rand() directly, * and could be called cl_rand(). * * If you want your own specific rand seed to re-generate a particular * sequence, call it once, throw away the return code, then call * srand(yourseed). Or don't use it anywhere in your code. */ int get_next_random(void); /* generate some random number in the range [a;b]; * typically used to randomly delay messages. */ #define HAVE_CL_RAND_FROM_INTERVAL 1 static inline int cl_rand_from_interval(const int a, const int b) { /* * Be careful here, you don't know RAND_MAX at coding time, * only at compile time. If you think * (int)(a + (rand()*(b-a)+(RAND_MAX/2))/RAND_MAX); * was correct, think again with RAND_MAX = INT_MAX, * which is the case for many rand() implementations nowadays. * * Don't do modulo, either, as that will skew the distribution, and * still has possible wraparounds, or an insufficient input set for too * small RAND_MAX. * * Rather do the whole calculation in 64 bit, which should be correct * as long as r, a, b, and RAND_MAX are all int. * Of course, if you prefer, you can do it with floating point as well. */ #if 1 /* use long long */ long long r = get_next_random(); r = a + (r * (b-a) + RAND_MAX/2)/RAND_MAX; #else /* use floating point */ int r = get_next_random(); r = a + (int)(1.0 / RAND_MAX * r * (b-a) + 0.5); #endif return r; } Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_reboot.h0000644000000000000000000000036012120057602027160 0ustar00usergroup00000000000000#ifndef CLPLUMBING_CL_REBOOT_H #define CLPLUMBING_CL_REBOOT_H 1 #include void cl_enable_coredump_before_reboot(gboolean yesno); /* not implemented in all OSes */ void cl_reboot(int msdelaybeforereboot, const char * reason); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_signal.h0000644000000000000000000000545612120057602027156 0ustar00usergroup00000000000000/* * cl_signal.h: signal handling routines to be used by Linux-HA programmes * * Copyright (C) 2002 Horms * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _CL_SIGNAL_H #define _CL_SIGNAL_H #include #include #include typedef struct { int sig; void (*handler)(int); int interrupt; } cl_signal_mode_t; #define CL_SIGNAL(_sig, _handler) \ cl_signal_set_simple_handler((_sig), (_handler), NULL) #if HAVE_SIGIGNORE #define CL_IGNORE_SIG(_sig) sigignore((_sig)) #else #define CL_IGNORE_SIG(_sig) CL_SIGNAL((_sig), SIG_IGN) #endif #define CL_DEFAULT_SIG(_sig) CL_SIGNAL((_sig), SIG_DFL) #define CL_SIGINTERRUPT(_sig, _flag) siginterrupt((_sig), (_flag)) #define CL_SIGACTION(_signum, _act, _oldact) \ sigaction((_signum), (_act), (_oldact)) #define CL_SIGPROCMASK(_how, _set, _oldset) \ cl_signal_block_set((_how), (_set), (_oldset)) #define CL_SIGPENDING(_set) sigpending(_set) #define CL_SIGSUSPEND(_mask) sigsuspend(_mask) #define CL_SIGEMPTYSET(_set) sigemptyset(_set) #define CL_SIGFILLSET(_set) sigfillset(_set) #define CL_SIGADDSET(_set, _signum) sigaddset((_set), (_signum)) #define CL_SIGDELSET(_set, _signum) sigdelset((_set), (_signum)) #define CL_SIGISMEMBER(_set, _signum) sigmember((_set), (_signum)) #define CL_KILL(_pid, _sig) kill((_pid), (_sig)) #define CL_PID_EXISTS(_pid) ( CL_KILL((_pid), 0) >= 0 || errno != ESRCH ) int cl_signal_set_handler(int sig, void (*handler)(int), sigset_t *mask , int flags, struct sigaction *oldact); int cl_signal_set_simple_handler(int sig, void (*handler)(int) , struct sigaction *oldact); int cl_signal_set_action(int sig, void (*action)(int, siginfo_t *, void *) , sigset_t *mask, int flags, struct sigaction *oldact); int cl_signal_set_simple_action(int sig, void (*action)(int, siginfo_t *, void *) , struct sigaction *oldact); int cl_signal_set_interrupt(int sig, int flag); int cl_signal_block(int how, int signal, sigset_t *oldset); int cl_signal_block_set(int how, const sigset_t *set, sigset_t *oldset); int cl_signal_set_handler_mode(const cl_signal_mode_t *mode, sigset_t *set); #endif /* _CL_SIGNAL_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_syslog.h0000644000000000000000000000222612120057602027211 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Functions to support syslog. * David Lee (c) 2005 */ #ifndef _CLPLUMBING_CL_SYSLOG_H #define _CLPLUMBING_CL_SYSLOG_H /* Convert string "auth" to equivalent number "LOG_AUTH" etc. */ int cl_syslogfac_str2int(const char *); /* Convert number "LOG_AUTH" to equivalent string "auth" etc. */ /* Returns static string; caller must NOT free. */ const char *cl_syslogfac_int2str(int); #endif /* _CLPLUMBING_CL_SYSLOG_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_tiebreaker.h0000644000000000000000000000217612120057602030012 0ustar00usergroup00000000000000/* * cl_tiebreaker.h: head file for tiebreaker module * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CL_TIEBREAKER_H_ #define _CL_TIEBREAKER_H_ #define HB_TIEBREAKER_TYPE tiebreaker #define HB_TIEBREAKER_TYPE_S "tiebreaker" /* * List of functions provided by implementations of tiebreaker interface. */ struct hb_tiebreaker_fns { gboolean (*break_tie) (int, int); }; #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cl_uuid.h0000644000000000000000000000271212120057602026637 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CL_UUID_H_ #define _CL_UUID_H_ #include typedef struct cl_uuid_s{ unsigned char uuid[16]; }cl_uuid_t; void cl_uuid_copy(cl_uuid_t* dst, cl_uuid_t* src); void cl_uuid_clear(cl_uuid_t* uu); int cl_uuid_compare(const cl_uuid_t* uu1, const cl_uuid_t* uu2); void cl_uuid_generate(cl_uuid_t* out); int cl_uuid_is_null(cl_uuid_t* uu); int cl_uuid_parse( char *in, cl_uuid_t* uu); #define UU_UNPARSE_SIZEOF 37 /* Including NULL byte */ void cl_uuid_unparse(const cl_uuid_t* uu, char *out); /* Suitable for ues as a GHashFunc from glib */ guint cl_uuid_g_hash(gconstpointer uuid_ptr); /* Suitable for ues as a GEqualFunc from glib */ gboolean cl_uuid_g_equal(gconstpointer uuid_ptr_a, gconstpointer uuid_ptr_b); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/coredumps.h0000644000000000000000000000266112120057602027217 0ustar00usergroup00000000000000/* * Basic Core dump control functions. * * Copyright (C) 2004 IBM Corporation * * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _CLPLUMBING_COREFILES_H # define _CLPLUMBING_COREFILES_H 1 /* Set the root directory of our core directory hierarchy */ int cl_set_corerootdir(const char * dir); /* Change directory to the directory our core file needs to go in */ /* Call after you establish the userid you're running under */ int cl_cdtocoredir(void); /* Enable/disable core dumps for ourselves and our child processes */ int cl_enable_coredumps(int truefalse); void cl_untaint_coredumps(void); void cl_set_coredump_signal_handler(int nsig); void cl_set_all_coredump_signal_handlers(void); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/cpulimits.h0000644000000000000000000000434412120057602027227 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Functions to put limits on CPU consumption. * This allows us to better catch runaway realtime processes that * might otherwise hang the whole system. * * The process is basically this: * - Set the CPU percentage limit with cl_cpu_limit_setpercent() * according to what you expect the CPU percentage to top out at * measured over an interval at >= 10 seconds * - Call cl_cpu_limit_ms_interval() to figure out how often to update * the CPU limit (it returns milliseconds) * - At least as often as indicated above, call cl_cpu_limit_update() * to update our current CPU limit. * * These limits are approximate, so be a little conservative. * If you've gone into an infinite loop, it'll likely get caught ;-) * * Note that exceeding the soft CPU limits we set here will cause a * SIGXCPU signal to be sent. * * The default action for this signal is to cause a core dump. * This is a good choice ;-) * * As of this writing, this code will never set the soft CPU limit less * than two seconds, or greater than 10 seconds. * * It will currrently return a limit update interval between 10000 and * 400000 milliseconds. * */ /* * Set expected CPU percentage upper bound */ int cl_cpu_limit_setpercent(int ipercent); /* * Update the current CPU limit */ int cl_cpu_limit_update(void); /* * How often should we call cl_cpu_limit_update()? * * Note: return result is in milliseconds */ int cl_cpu_limit_ms_interval(void); /* Disable further CPU limits... */ int cl_cpu_limit_disable(void); Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/ipc.h0000644000000000000000000005416712120057602026001 0ustar00usergroup00000000000000/* * ipc.h IPC abstraction data structures. * * author Xiaoxiang Liu , * Alan Robertson * * * Copyright (c) 2002 International Business Machines * Copyright (c) 2002 Xiaoxiang Liu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IPC_H_ #define _IPC_H_ #include #undef MIN #undef MAX #include #include #ifdef IPC_TIME_DEBUG #include #define MAXIPCTIME 3000 #endif /* constants */ #define DEFAULT_MAX_QLEN 64 #define MAX_MSGPAD 128 /* channel and connection status */ #define IPC_CONNECT 1 /* Connected: can read, write */ #define IPC_WAIT 2 /* Waiting for connection */ #define IPC_DISCONNECT 3 /* Disconnected, can't read or write*/ #define IPC_DISC_PENDING 4 /* Disconnected, can't write but */ /* may be more data to read */ #define MAXFAILREASON 128 #define IPC_SERVER 1 #define IPC_CLIENT 2 #define IPC_PEER 3 #define IPC_ISRCONN(ch) ((ch)->ch_status == IPC_CONNECT \ || (ch)->ch_status == IPC_DISC_PENDING) #define IPC_ISWCONN(ch) ((ch)->ch_status == IPC_CONNECT) /* general return values */ #define IPC_OK 0 #define IPC_FAIL 1 #define IPC_BROKEN 2 #define IPC_INTR 3 #define IPC_TIMEOUT 4 /* * IPC: Sockets-like Interprocess Communication Abstraction * * We have two fundamental abstractions which we maintain. * Everything else is in support of these two abstractions. * * These two main abstractions are: * * IPC_WaitConnection: * A server-side abstraction for waiting for someone to connect. * * IPC_Channel: * An abstraction for an active communications channel. * * All the operations on these two abstractions are carried out * via function tables (channel->ops). Below we refer to the * function pointers in these tables as member functions. * * On the server side, everything starts up with a call to * ipc_wait_conn_constructor(), which returns an IPC_WaitConnection. * * Once the server has the IPC_WaitConnection object in hand, * it can give the result of the get_select_fd() member function * to poll or select to inform you when someone tries to connect. * * Once select tells you someone is trying to connect, you then * use the accept_connection() member function to accept * the connection. accept_connection() returns an IPC_Channel. * * With that, the server can talk to the client, and away they * go ;-) * * On the client side, everything starts up with a call to * ipc_channel_constructor() which we use to talk to the server. * The client is much easier ;-) */ typedef struct IPC_WAIT_CONNECTION IPC_WaitConnection; typedef struct IPC_CHANNEL IPC_Channel; typedef struct IPC_MESSAGE IPC_Message; typedef struct IPC_QUEUE IPC_Queue; typedef struct IPC_AUTH IPC_Auth; typedef struct IPC_OPS IPC_Ops; typedef struct IPC_WAIT_OPS IPC_WaitOps; /* wait connection structure. */ struct IPC_WAIT_CONNECTION{ int ch_status; /* wait conn. status.*/ void * ch_private; /* wait conn. private data. */ IPC_WaitOps *ops; /* wait conn. function table .*/ }; typedef void(*flow_callback_t)(IPC_Channel*, void*); /* channel structure.*/ struct IPC_CHANNEL{ int ch_status; /* identify the status of channel.*/ int refcount; /* reference count */ pid_t farside_pid; /* far side pid */ void* ch_private; /* channel private data. */ /* (may contain conn. info.) */ IPC_Ops* ops; /* IPC_Channel function table.*/ /* number of bytes needed * at the begginging of ->msg_body * it's for msg head needed to tranmit in wire */ unsigned int msgpad; /* the number of bytes remainng to send for the first message in send queue 0 means nothing has been sent thus all bytes needs to be send n != 0 means there are still n bytes needs to be sent */ unsigned int bytes_remaining; /* is the send blocking or nonblocking*/ gboolean should_send_block; /* if send would block, should an error be returned or not */ gboolean should_block_fail; /* There are two queues in channel. One is for sending and the other * is for receiving. * Those two queues are channel's internal queues. They should not be * accessed directly. */ /* private: */ IPC_Queue* send_queue; IPC_Queue* recv_queue; /* buffer pool for receive in this channel*/ struct ipc_bufpool* pool; /* the follwing is for send flow control*/ int high_flow_mark; int low_flow_mark; void* high_flow_userdata; void* low_flow_userdata; flow_callback_t high_flow_callback; flow_callback_t low_flow_callback; int conntype; char failreason[MAXFAILREASON]; /* New members to support Multi-level ACLs for the CIB, * available since libplumb.so.2.1.0, added at the * end of the struct to maintain backwards ABI compatibility. * * If you don't like to care for library versions, * create your IPC channels with * c = ipc_wait_conn_constructor(IPC_UDS_CRED, ...), * and these members will be available. */ uid_t farside_uid; /* far side uid */ gid_t farside_gid; /* far side gid */ }; struct IPC_QUEUE{ size_t current_qlen; /* Current qlen */ size_t max_qlen; /* Max allowed qlen */ GList* queue; /* List of messages */ /* keep the time of the last max queue warning */ time_t last_maxqlen_warn; /* and the number of messages lost */ unsigned maxqlen_cnt; }; /* authentication information : set of gids and uids */ struct IPC_AUTH { GHashTable * uid; /* hash table for user id */ GHashTable * gid; /* hash table for group id */ }; /* Message structure. */ struct IPC_MESSAGE{ size_t msg_len; void* msg_buf; void* msg_body; /* * IPC_MESSAGE::msg_done * the callback function pointer which can be called after this * message is sent, received or otherwise processed. * * Parameter: * msg: the back pointer to the message which contains this * function pointer. * */ void (* msg_done)(IPC_Message * msg); void* msg_private; /* the message private data. */ /* Belongs to message creator */ /* May be used by callback function. */ IPC_Channel * msg_ch; /* Channel the */ /* message is from/in */ }; struct IPC_WAIT_OPS{ /* * IPC_WAIT_OPS::destroy * destroy the wait connection and free the memory space used by * this wait connection. * * Parameters: * wait_conn (IN): the pointer to the wait connection. * */ void (* destroy)(IPC_WaitConnection *wait_conn); /* * IPC_WAIT_OPS::get_select_fd * provide a fd which user can listen on for a new coming connection. * * Parameters: * wait_conn (IN) : the pointer to the wait connection which * we're supposed to return the file descriptor for * (the file descriptor can be used with poll too ;-)) * * Return values: * integer >= 0 : the select_fd. * -1 : can't get the select fd. * */ int (* get_select_fd)(IPC_WaitConnection *wait_conn); /* * IPC_WAIT_OPS::accept_connection * accept and create a new connection and verify the authentication. * * Parameters: * wait_conn (IN) : the waiting connection which will accept * create the new connection. * auth_info (IN) : the authentication information which will be * verified for the new connection. * * Return values: * the pointer to the new IPC channel; NULL if the creation or * authentication fails. * */ IPC_Channel * (* accept_connection) (IPC_WaitConnection * wait_conn, IPC_Auth *auth_info); }; /* Standard IPC channel operations */ struct IPC_OPS{ /* * IPC_OPS::destroy * brief destroy the channel object. * * Parameters: * ch (IN) : the pointer to the channel which will be destroyed. * */ void (*destroy) (IPC_Channel * ch); /* * IPC_OPS::initiate_connection * used by service user side to set up a connection. * * Parameters: * ch (IN) : the pointer to channel used to initiate the connection. * * Return values: * IPC_OK : the channel set up the connection successfully. * IPC_FAIL : the connection initiation fails. * */ int (* initiate_connection) (IPC_Channel * ch); /* * IPC_OPS::verify_auth * used by either side to verify the identity of peer on connection. * * Parameters * ch (IN) : the pointer to the channel. * * Return values: * IPC_OK : the peer is trust. * IPC_FAIL : verifying authentication fails. */ int (* verify_auth) (IPC_Channel * ch, IPC_Auth* info); /* * IPC_OPS::assert_auth * service user asserts to be certain qualified service user. * * Parameters: * ch (IN): the active channel. * auth (IN): the hash table which contains the asserting information. * * Return values: * IPC_OK : assert the authentication successfully. * IPC_FAIL : assertion fails. * * NOTE: This operation is a bit obscure. It isn't needed with * UNIX domain sockets at all. The intent is that some kinds * of IPC (like FIFOs), do not have an intrinsic method to * authenticate themselves except through file permissions. * The idea is that you must tell it how to chown/grp your * FIFO so that the other side and see that if you can write * this, you can ONLY be the user/group they expect you to be. * But, I think the parameters may be wrong for this ;-) */ int (* assert_auth) (IPC_Channel * ch, GHashTable * auth); /* * IPC_OPS::send * send the message through the sending connection. * * Parameters: * ch (IN) : the channel which contains the connection. * msg (IN) : pointer to the sending message. User must * allocate the message space. * * Return values: * IPC_OK : the message was either sent out successfully or * appended to the send_queue. * IPC_FAIL : the send operation failed. * IPC_BROKEN : the channel is broken. * */ int (* send) (IPC_Channel * ch, IPC_Message* msg); /* * IPC_OPS::recv * receive the message through receving queue. * * Parameters: * ch (IN) : the channel which contains the connection. * msg (OUT): the IPC_MESSAGE** pointer which contains the pointer * to the received message or NULL if there is no * message available. * * Return values: * IPC_OK : receive operation is completed successfully. * IPC_FAIL : operation failed. * IPC_BROKEN : the channel is broken (disconnected) * * Note: * return value IPC_OK doesn't mean the message is already * sent out to (or received by) the peer. It may be pending * in the send_queue. In order to make sure the message is no * longer needed, please specify the msg_done function in the * message structure and once this function is called, the * message is no longer needed. * * is_sending_blocked() is another way to check if there is a message * pending in the send_queue. * */ int (* recv) (IPC_Channel * ch, IPC_Message** msg); /* * IPC_OPS::waitin * Wait for input to become available * * Parameters: * ch (IN) : the channel which contains the connection. * * Side effects: * If output becomes unblocked while waiting, it will automatically * be resumed without comment. * * Return values: * IPC_OK : a message is pending or output has become unblocked. * IPC_FAIL : operation failed. * IPC_BROKEN : the channel is broken (disconnected) * IPC_INTR : waiting was interrupted by a signal */ int (* waitin) (IPC_Channel * ch); /* * IPC_OPS::waitout * Wait for output to finish * * Parameters: * ch (IN) : the channel which contains the connection. * * Side effects: * If input becomes available while waiting, it will automatically * be read into the channel queue without comment. * * Return values: * IPC_OK : output no longer blocked * IPC_FAIL : operation failed. * IPC_BROKEN : the channel is broken (disconnected) * IPC_INTR : waiting was interrupted by a signal */ int (* waitout) (IPC_Channel * ch); /* * IPC_OPS::is_message_pending * check to see if there is any messages ready to read, or hangup has * occurred. * * Parameters: * ch (IN) : the pointer to the channel. * * Return values: * TRUE : there are messages ready to read, or hangup. * FALSE: there are no messages ready to be read. */ gboolean (* is_message_pending) (IPC_Channel * ch); /* * IPC_OPS::is_sending_blocked * check the send_queue to see if there are any messages blocked. * * Parameters: * ch (IN) : the pointer to the channel. * * Return values: * TRUE : there are messages blocked (waiting) in the send_queue. * FALSE: there are no message blocked (waiting) in the send_queue. * * See also: * get_send_select_fd() */ gboolean (* is_sending_blocked) (IPC_Channel *ch); /* * IPC_OPS::resume_io * Resume all possible IO operations through the IPC transport * * Parameters: * the pointer to the channel. * * Return values: * IPC_OK : resume all the possible I/O operation successfully. * IPC_FAIL : the operation fails. * IPC_BROKEN : the channel is broken. * */ int (* resume_io) (IPC_Channel *ch); /* * IPC_OPS::get_send_select_fd() * return a file descriptor which can be given to select/poll. This fd * is used by the IPC code for sending. It is intended that this be * ONLY used with select, poll, or similar mechanisms, not for direct I/O. * Note that due to select(2) and poll(2) semantics, you must check * is_sending_blocked() to see whether you should include this FD in * your poll for writability, or you will loop very fast in your * select/poll loop ;-) * * Parameters: * ch (IN) : the pointer to the channel. * * Return values: * integer >= 0 : the send fd for selection. * -1 : there is no send fd. * * See also: * is_sending_blocked() */ int (* get_send_select_fd) (IPC_Channel * ch); /* * IPC_OPS::get_recv_select_fd * return a file descriptor which can be given to select. This fd * is for receiving. It is intended that this be ONLY used with select, * poll, or similar mechanisms, NOT for direct I/O. * * Parameters: * ch (IN) : the pointer to the channel. * * Return values: * integer >= 0 : the recv fd for selection. * -1 : there is no recv fd. * * NOTE: This file descriptor is often the same as the send * file descriptor. */ int (* get_recv_select_fd) (IPC_Channel * ch); /* * IPC_OPS::set_send_qlen * allow user to set the maximum send_queue length. * * Parameters * ch (IN) : the pointer to the channel. * q_len (IN) : the max length for the send_queue. * * Return values: * IPC_OK : set the send queue length successfully. * IPC_FAIL : there is no send queue. (This isn't supposed * to happen). * It means something bad happened. * */ int (* set_send_qlen) (IPC_Channel * ch, int q_len); /* * IPC_OPS::set_recv_qlen * allow user to set the maximum recv_queue length. * * Parameters: * ch (IN) : the pointer to the channel. * q_len (IN) : the max length for the recv_queue. * * Return values: * IPC_OK : set the recv queue length successfully. * IPC_FAIL : there is no recv queue. * */ int (* set_recv_qlen) (IPC_Channel * ch, int q_len); /* * IPC_OPS: set callback for high/low flow mark * ch (IN) : the pointer to the channel * callback (IN) : the callback function * user_data(IN) : a pointer to user_data * callback will be called with channel and * this user_data as parameters * * Return values: * void * */ void (* set_high_flow_callback) (IPC_Channel* ch , flow_callback_t callback, void* user_data); void (* set_low_flow_callback) (IPC_Channel* ch , flow_callback_t callback, void* user_data); /* * IPC_OPS::new_ipcmsg * ch (IN) : the pointer to the channel * data (IN) : data to be copied to the message body * len (IN) : data len * private (IN): the pointer value to set as in the message * * Return values: * the pointer to a new created message will be * returned if success or NULL if failure * */ IPC_Message* (*new_ipcmsg)(IPC_Channel* ch, const void* data, int len, void* private); /* * IPC_OPS::nget_chan_status * ch (IN) : the pointer to the channel * * Return value: * channel status. * */ int (*get_chan_status)(IPC_Channel* ch); /* * These two functions returns true if the corresponding queue * is full, otherwise it returns false */ gboolean (*is_sendq_full)(struct IPC_CHANNEL * ch); gboolean (*is_recvq_full)(struct IPC_CHANNEL * ch); /* Get the connection type for the channel * it can be IPC_SERVER, IPC_CLIENT, IPC_PEER */ int (*get_conntype)(struct IPC_CHANNEL* ch); int (*disconnect)(struct IPC_CHANNEL* ch); }; /* * ipc_wait_conn_constructor: * the common constructor for ipc waiting connection. * Use ch_type to identify the connection type. Usually it's only * needed by server side. * * Parameters: * ch_type (IN) : the type of the waiting connection to create. * ch_attrs (IN) : the hash table which contains the attributes * needed by this waiting connection in name/value * pair format. * * For example, the only attribute needed by UNIX * domain sockets is path name. * * Return values: * the pointer to a new waiting connection or NULL if the connection * can't be created. * Note: * current implementation supports * IPC_ANYTYPE: This is what program code should typically use. * Internally it is an alias to IPC_UDS_CRED. * IPC_UDS_CRED: Unix Domain Sockets, * farside uid + gid credentials is available. * Available since libplumb.so.2.1.0. * IPC_DOMAIN_SOCKET: An other alias to Unix Domain Sockets; * internally it is equivalent to both above. * Using this explicitly, your code will work * even with libplumb.so.2.0.0. * Which also means that you MUST NOT use the * farside_uid/gid functionality then. */ extern IPC_WaitConnection * ipc_wait_conn_constructor(const char * ch_type , GHashTable* ch_attrs); /* * ipc_channel_constructor: * brief the common constructor for ipc channel. * Use ch_type to identify the channel type. * Usually this function is only called by client side. * * Parameters: * ch_type (IN): the type of the channel you want to create. * ch_attrs (IN): the hash table which contains the attributes needed * by this channel. * For example, the only attribute needed by UNIX domain * socket is path name. * * Return values: * the pointer to the new channel whose status is IPC_DISCONNECT * or NULL if the channel can't be created. * * Note: * See comments for ipc_wait_conn_constructor above * for currently implemented ch_type channel types. */ extern IPC_Channel * ipc_channel_constructor(const char * ch_type , GHashTable* ch_attrs); /* * ipc_channel_pair: * Construct a pair of connected IPC channels in a fashion analogous * to pipe(2) or socketpair(2). * * Parameters: * channels: an array of two IPC_Channel pointers for return result */ int ipc_channel_pair(IPC_Channel* channels[2]); /* * ipc_set_auth: * A helper function used to convert array of uid and gid into * an authentication structure (IPC_Auth) * * Parameters: * a_uid (IN): the array of a set of user ids. * a_gid (IN): the array of a set of group ids. * num_uid (IN): the number of user ids. * num_gid (IN): the number of group ids. * * Return values: * the pointer to the authentication structure which contains the * set of uid and the set of gid. Or NULL if this structure can't * be created. * */ IPC_Auth* ipc_str_to_auth(const char * uidlist, int, const char * gidlist, int); extern IPC_Auth * ipc_set_auth(uid_t * a_uid, gid_t * a_gid , int num_uid, int num_gid); /* Destroys an object constructed by ipc_set_auth or ipc_str_to_auth() */ extern void ipc_destroy_auth(IPC_Auth * auth); extern void ipc_set_pollfunc(int (*)(struct pollfd*, unsigned int, int)); extern void ipc_bufpool_dump_stats(void); #ifdef IPC_TIME_DEBUG enum MSGPOS_IN_IPC{ MSGPOS_ENQUEUE, MSGPOS_SEND, MSGPOS_RECV, MSGPOS_DEQUEUE }; #endif struct SOCKET_MSG_HEAD{ int msg_len; unsigned int magic; #ifdef IPC_TIME_DEBUG longclock_t enqueue_time; longclock_t send_time; longclock_t recv_time; longclock_t dequeue_time; #endif }; /* MAXMSG is the maximum final message size on the wire. */ #define MAXMSG (256*1024) /* MAXUNCOMPRESSED is the maximum, raw data size prior to compression. */ /* 1:8 compression ratio is to be expected on data such as xml */ #define MAXUNCOMPRESSED (2048*1024) #define HEADMAGIC 0xabcd #define POOL_SIZE (4*1024) struct ipc_bufpool{ int refcount; char* currpos; char* consumepos; char* startpos; char* endpos; int size; }; struct ipc_bufpool* ipc_bufpool_new(int); void ipc_bufpool_del(struct ipc_bufpool* pool); int ipc_bufpool_spaceleft(struct ipc_bufpool* pool); int ipc_bufpool_update(struct ipc_bufpool* pool, struct IPC_CHANNEL * ch, int msg_len, IPC_Queue* rqueue); gboolean ipc_bufpool_full(struct ipc_bufpool* pool, struct IPC_CHANNEL* ch, int*); int ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool, struct ipc_bufpool* srcpool); void ipc_bufpool_ref(struct ipc_bufpool* pool); void ipc_bufpool_unref(struct ipc_bufpool* pool); void set_ipc_time_debug_flag(gboolean flag); /* pathname attribute */ #define IPC_PATH_ATTR "path" /* socket mode attribute */ #define IPC_MODE_ATTR "sockmode" /* Unix domain socket, used by old code. * See also the comment block above ipc_wait_conn_constructor() */ #define IPC_DOMAIN_SOCKET "uds" /* Unix domain socket with farside uid + gid credentials. * Available since libplumb.so.2.1.0 */ #define IPC_UDS_CRED "uds_c" #ifdef IPC_UDS_CRED # define IPC_ANYTYPE IPC_UDS_CRED #else # error "No IPC types defined(!)" #endif #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/loggingdaemon.h0000644000000000000000000000216012120057602030022 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Messages sent to the logging daemon */ #define LD_LOGIT 2 #define MAXENTITY 64 /* Message contains following header, followed by the text (char[]) itself */ struct LogDaemonMsgHdr_s { int msgtype; int facility; int priority; int msglen; gboolean use_pri_str; int entity_pid; char entity[MAXENTITY]; TIME_T timestamp; }; typedef struct LogDaemonMsgHdr_s LogDaemonMsgHdr; Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/longclock.h0000644000000000000000000001031612120057602027165 0ustar00usergroup00000000000000/* * Longclock operations * * Copyright (c) 2002 International Business Machines * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _LONGCLOCK_H # define _LONGCLOCK_H /* * A longclock_t object is a lot like a clock_t object, except that it * won't wrap in the lifetime of the earth. It is guaranteed to be at * least 64 bits. This means it should go for around 2 billion years. * * It is also supposed to be proof against changes in the local time on * the computer. This is easy if you have a properly-working times(2) * for us to use. * * longclock_t's are definitely not comparable between computers, and in * some implementations, not even between processes on the same computer. * * * The functions provided here are: * * unsigned long cl_times(void); * A rational wrapper for the times(2) call * for those cases where only the return value * is wanted. * longclock_t time_longclock(void); * Returns current time as a longclock_t. * * longclock_t msto_longclock(unsigned long); * Converts quantity in milliseconds to longclock_t * * unsigned long longclockto_ms(longclock_t); * Converts quantity in longclock_t to milliseconds * NOTE: Can overflow! * * unsigned long longclockto_long(longclock_t); * Converts quantity in longclock_t to clock_t * NOTE: Can overflow! * * longclock_t secsto_longclock(unsigned long); * Converts quantity in seconds to longclock_t * * longclock_t add_longclock(longclock_t l, longclock_t r); * Adds two longclock_t values * * int cmp_longclock(longclock_t l, longclock_t r); * Returns negative, zero or positive value * * longclock_t sub_longclock(longclock_t l, longclock_t r); * Subtracts two longclock_t values * NOTE: Undefined if l is < r * * longclock_t dsecsto_longclock(double); * Converts quantity in seconds (as a double) * to a longclock_t * * unsigned hz_longclock(void); * Returns frequency of longclock_t clock. * * We provide this constant: * * extern const longclock_t zero_longclock; */ extern unsigned long cl_times(void); #ifdef CLOCK_T_IS_LONG_ENOUGH # ifndef HAVE_LONGCLOCK_ARITHMETIC # define HAVE_LONGCLOCK_ARITHMETIC # endif # include typedef clock_t longclock_t; #else /* clock_t isn't at least 64 bits */ typedef unsigned long long longclock_t; #endif longclock_t time_longclock(void); extern const longclock_t zero_longclock; unsigned hz_longclock(void); longclock_t secsto_longclock(unsigned long); longclock_t dsecsto_longclock(double); longclock_t msto_longclock(unsigned long); unsigned long longclockto_ms(longclock_t); /* Can overflow! */ long longclockto_long(longclock_t); /* May overflow! */ #ifndef HAVE_LONGCLOCK_ARITHMETIC longclock_t add_longclock(longclock_t l, longclock_t r); /* Undefined if l is < r according to cmp_longclock() */ longclock_t sub_longclock(longclock_t l, longclock_t r); int cmp_longclock(longclock_t l, longclock_t r); #else /* We HAVE_LONGCLOCK_ARITHMETIC */ # define longclockto_long(lc) ((long)(lc)) # define add_longclock(l,r) \ ((longclock_t)(l) + (longclock_t)(r)) # define sub_longclock(l,r) \ ((longclock_t)(l) - (longclock_t)(r)) # define cmp_longclock(l,r) \ (((longclock_t)(l) < (longclock_t)(r)) \ ? -1 \ : (((longclock_t)(l) > (longclock_t)(r)) \ ? +1 : 0)) #endif /* N.B: Possibly not the best place for this, but it will do for now */ /* This is consistent with OpenBSD, and is a good choice anyway */ #define TIME_T unsigned long #define TIME_F "%lu" #define TIME_X "%lx" #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/lsb_exitcodes.h0000644000000000000000000000657312120057602030053 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* LSB status exit codes. * * All of these and the supporting text are taken from the LSB. * * If the status command is given, the init script will return * the following exit status codes. * * 0 program is running or service is OK * 1 program is dead and /var/run pid file exists * 2 program is dead and /var/lock lock file exists * 3 program is stopped * 4 program or service status is unknown * 5-99 reserved for future LSB use * 100-149 reserved for distribution use * 150-199 reserved for application use * 200-254 reserved */ #define LSB_STATUS_OK 0 #define LSB_STATUS_VAR_PID 1 #define LSB_STATUS_VAR_LOCK 2 #define LSB_STATUS_STOPPED 3 #define LSB_STATUS_UNKNOWN 4 #define LSB_STATUS_LSBRESERVED 5 #define LSB_STATUS_DISTRESERVED 100 #define LSB_STATUS_APPRESERVED 150 #define LSB_STATUS_RESERVED 200 /* * * In the case of init script commands other than "status" * (i.e., "start", "stop", "restart", "reload", and "force-reload"), * the init script must return an exit status of zero if the action * described by the argument has been successful. Otherwise, the * exit status shall be non-zero, as defined below. In addition * to straightforward success, the following situations are also * to be considered successful: * * restarting a service (instead of reloading it) with the * "force-reload" argument * running "start" on a service already running * running "stop" on a service already stopped or not running * running "restart" on a service already stopped or not running * In case of an error, while processing any init script action * except for "status", the init script must print an error * message and return one of the following non-zero exit * status codes. * * 1 generic or unspecified error (current practice) * 2 invalid or excess argument(s) * 3 unimplemented feature (for example, "reload") * 4 user had insufficient privilege * 5 program is not installed * 6 program is not configured * 7 program is not running * 8-99 reserved for future LSB use * 100-149 reserved for distribution use * 150-199 reserved for application use * 200-254 reserved * * All error messages must be printed on standard error. * All status messages must be printed on standard output. * (This does not prevent scripts from calling the logging * functions such as log_failure_msg). */ #define LSB_EXIT_OK 0 #define LSB_EXIT_GENERIC 1 #define LSB_EXIT_EINVAL 2 #define LSB_EXIT_ENOTSUPPORTED 3 #define LSB_EXIT_EPERM 4 #define LSB_EXIT_NOTINSTALLED 5 #define LSB_EXIT_NOTCONFIGED 6 #define LSB_EXIT_NOTRUNNING 7 #define LSB_EXIT_LSBRESERVED 8 #define LSB_EXIT_DISTRESERVED 100 #define LSB_EXIT_APPRESERVED 150 #define LSB_EXIT_RESERVED 200 Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/md5.h0000644000000000000000000000304412120057602025677 0ustar00usergroup00000000000000/* * md5.h: MD5 and keyed-MD5 algorithms * * Author: Sun Jiang Dong * Copyright (c) 2005 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _MD5_H_ #define _MD5_H__ /* * MD5: The MD5 Message-Digest Algorithm ( RFC 1321 ) * return value: 0 - success * <0 - fail * Note: The digest buffer should be not less than 16. * */ int MD5( const unsigned char *data , unsigned long data_len , unsigned char * digest); /* * HMAC: Keyed-Hashing for Message Authentication * return value: 0 - success * <0 - fail * Note: The digest buffer should be not less than 16. */ int HMAC( const unsigned char * key , unsigned int key_len , const unsigned char * data , unsigned long data_len , unsigned char * digest); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/mkstemp_mode.h0000644000000000000000000000253012120057602027675 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * A slightly safer version of mkstemp(3) * * In this version, the file is initially created mode 0, (using umask) and * then chmod-ed to the requested permissions after calling mkstemp(3). * This guarantees that the file is not even momentarily open beyond the * requested permissions. * * Return values: * * Like mkstemp, it returns the file descriptor of the open file, or -1 * on error. * * In addition to the errno values documented for mkstemp(3), this functio * can also fail with any of the errno values documented for chmod(2). * */ int mkstemp_mode(char* template, mode_t requested_filemode); Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/netstring.h0000644000000000000000000000311212120057602027223 0ustar00usergroup00000000000000/* * Intracluster message object (struct ha_msg) * * Copyright (C) 1999, 2000 Guochun Shi * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NET_STRING_H #define NET_STRING_H #include #include #include extern gboolean cl_msg_quiet_fmterr; /* Convert a message to netstring data */ char* msg2netstring(const struct ha_msg*, size_t*); char * msg2netstring_noauth(const struct ha_msg *m, size_t * slen); /* Convert netstring data to a message */ struct ha_msg * netstring2msg(const char*, size_t, int); /* Is this netstring authentic? */ int is_auth_netstring(const char* datap, size_t datalen, const char* authstring, size_t authlen); void cl_set_authentication_computation_method(int (*method)(int authmethod , const void * data , size_t datalen , char * authstr , size_t authlen)); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/proctrack.h0000644000000000000000000000705112120057602027204 0ustar00usergroup00000000000000/* * Process tracking object. * * Copyright (c) 2002 International Business Machines * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _PROCTRACK_H # define _PROCTRACK_H #include #include #include /* * We track processes, mainly so we can do something appropriate * when they die, and find processes should we need to kill them... */ typedef struct _ProcTrack ProcTrack; typedef struct _ProcTrack_ops ProcTrack_ops; typedef struct _ProcTrackKillInfo ProcTrackKillInfo; /* * The levels of logging possible for our process */ enum _ProcTrackLogType { PT_LOGNONE = 2, /* Exits never automatically logged */ PT_LOGNORMAL, /* Automatically log abnormal exits */ PT_LOGVERBOSE /* Automatically log every exit */ }; typedef enum _ProcTrackLogType ProcTrackLogType; #define proctrack_pid(p) (p)->pid #define proctrack_data(p) (p)->privatedata #define reset_proctrack_data(p) (p)->privatedata = NULL #define proctrack_timedout(p) ((p)->timeoutseq > 0) struct _ProcTrack { pid_t pid; int isapgrp; ProcTrackLogType loglevel; void * privatedata; ProcTrack_ops* ops; longclock_t startticks; TIME_T starttime; unsigned timerid; int timeoutseq; ProcTrackKillInfo* killinfo; }; /* * The set of operations to perform on our tracked processes. */ struct _ProcTrack_ops { /* Called when a process dies */ void (*procdied) (ProcTrack* p, int status, int signo, int exitcode , int waslogged); /* Called when a process registers */ void (*procregistered) (ProcTrack*p); /* Returns a "name" for a process (for messages) */ /* (may have to be copied, because it may be a static value) */ const char * (*proctype) (ProcTrack* p); }; struct _ProcTrackKillInfo { long mstimeout; /* Timeout in milliseconds */ int signalno; /* Signal number to issue @ timeout */ }; /* A function for calling by the process table iterator */ typedef void (*ProcTrackFun) (ProcTrack* p, void * data); /* Call this function to activate the procdied member function */ /* Returns TRUE if 'pid' was registered */ int ReportProcHasDied(int pid, int status); /* Create/Log a new tracked process */ void NewTrackedProc(pid_t pid, int isapgrp, ProcTrackLogType loglevel , void * privatedata , ProcTrack_ops* ops); /* "info" is 0-terminated (terminated by a 0 signal) */ int SetTrackedProcTimeouts(pid_t pid, ProcTrackKillInfo* info); void RemoveTrackedProcTimeouts(pid_t pid); /* Return information associated with the given PID (or NULL) */ ProcTrack* GetProcInfo(pid_t pid); /* * Iterate over the set of tracked processes. * If proctype is NULL, then walk through them all, otherwise only those * of the given type ("f") */ void ForEachProc(ProcTrack_ops* proctype, ProcTrackFun f, void * data); void DisableProcLogging(void); /* Useful for shutdowns */ void EnableProcLogging(void); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/realtime.h0000644000000000000000000000440412120057602027015 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_REALTIME_H # define _CLPLUMBING_REALTIME_H # include #if defined(SCHED_RR) && defined(_POSIX_PRIORITY_SCHEDULING) && !defined(ON_DARWIN) # define DEFAULT_REALTIME_POLICY SCHED_RR #endif /* * * make_realtime() will make the current process a soft realtime process * and lock it into memory after growing the heap by heapgrowK*1024 bytes * * If you set spolicy or priority to <= 0, then defaults will be used. * Otherwise you need to use a value for spolicy from * and use an appropriate priority for the given spolicy. * * WARNING: badly behaved programs which use the make_realtime() function * can easily hang the machine. */ void cl_make_realtime ( int spolicy, /* SCHED_RR or SCHED_FIFO (or SCHED_OTHER) */ int priority, /* typically 1-99 */ int stackgrowK, /* Amount to grow stack by */ int heapgrowK /* Amount to grow heap by */ ); void cl_make_normaltime(void); /* Cause calls to make_realtime() to be ignored */ void cl_disable_realtime(void); /* Cause calls to make_realtime() to be accepted. * This is the default behaviour */ void cl_enable_realtime(void); /* Sleep a really short (the shortest) time */ int cl_shortsleep(void); /* Print messages if we've done (more) non-realtime mallocs */ void cl_realtime_malloc_check(void); /* Number of times we "go to the well" for memory after becoming realtime */ int cl_nonrealtime_malloc_count(void); /* Number of bytes we "got from the well" for memory after becoming realtime */ unsigned long cl_nonrealtime_malloc_size(void); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/replytrack.h0000644000000000000000000001660212120057602027376 0ustar00usergroup00000000000000/* * Process tracking object. * * Copyright (c) 2007 Alan Robertson * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _REPLYTRACK_H # define _REPLYTRACK_H #include #include #include #include /* * We track replies - so we can tell when all expected replies were received. * * There is a problem in clusters where a message is sent to each node, and a * reply is expected from each node of knowing when all the replies have been * received. * * If all nodes are up, it's easy to see when all replies are received. * But, if some nodes are down, we really don't want to wait for a timeout * before we decide that we've gotten all the replies we're going to get, * since nodes can be down for potentially very long periods of time, and * waiting for a long timeout can delay things a great deal again and * again - causing significant delays and user frustration. * * That's where these functions come in! * Instead, inform these functions what nodes are up and what ones are down, * and when you receive a reply, and it will tell you when you've gotten * them all - managing all that tedious bookwork for you. */ typedef enum _replytrack_completion_type replytrack_completion_type_t; typedef enum _nodetrack_change nodetrack_change_t; typedef struct _replytrack replytrack_t; typedef struct _nodetrack nodetrack_t; typedef struct _nodetrack_intersection nodetrack_intersection_t; /* * The levels of logging possible for our process */ enum _replytrack_completion_type { REPLYT_ALLRCVD = 2, /* All replies received */ REPLYT_TIMEOUT, /* Timeout occurred with replies missing */ }; typedef void (*replytrack_callback_t) ( replytrack_t * rl , gpointer user_data , replytrack_completion_type_t reason); typedef void (*replytrack_iterator_t) ( replytrack_t* rl , gpointer user_data , const char* node , cl_uuid_t uuid); typedef void (*nodetrack_iterator_t) ( nodetrack_t* rl , gpointer user_data , const char* node , cl_uuid_t uuid); /* * Note: * If you use the timeout feature of this code, it relies on you using glib mainloop * for your scheduling. timeout_ms should be zero for no timeout. */ replytrack_t* replytrack_new(nodetrack_t* membership , replytrack_callback_t callback , unsigned long timeout_ms , gpointer user_data); void replytrack_del(replytrack_t *rl); gboolean replytrack_gotreply(replytrack_t *rl , const char * node , cl_uuid_t uuid); /* Returns TRUE if this was the final expected reply */ /* * Iterate over the set of outstanding replies: * return count of how many items in the iteration */ int replytrack_outstanding_iterate(replytrack_t* rl , replytrack_iterator_t i, gpointer user_data); int replytrack_outstanding_count(replytrack_t* rl); /* * The functions above operate using a view of membership which is established * through the functions below. * * This can either be through the heartbeat low-level membership API, or any * other view of membership you wish. Mentioning a node as either up or down * will automatically add that node to our view of potential membership. * * These functions only support one view of membership per process. * * The general idea of how to use these functions: * Initially: * 1) iterate through init membership and call nodetrack_node(up|down) for * each node to start things off. * * On an ongoing basis: * 2) call nodetrack_node_up whenever a node comes up * We expect a reply from nodes that are up. * 3) call nodetrack_node_down whenever a node goes down * We don't expect a reply from nodes that are down. * * For each set of replies you want tracked: * 4) Create a replytrack_t for a set of expected replies * 5) call replytrack_gotreply() each time you get an expected reply * 6) replist_gotreply() returns TRUE when the final message was received. * (it does this by comparing against the membership as defined below) * 7) you will get a callback when timeout occurs or final message is received * n. b.: * No callback function => manage timeouts yourself * 8) call replytrack_del() when you're done with the reply list * n. b.: * If you have replies outstanding, and you have a timeout and * a callback function set, you will get a warning for destroying * a replytrack_t object 'prematurely'. * You will also log a warning if you call replytrack_gotreply() after * all replies were received or a timeout occurred. * */ /* * The levels of logging possible for our process */ enum _nodetrack_change { NODET_UP = 2, /* This node came up */ NODET_DOWN, /* This node went down */ }; typedef void (*nodetrack_callback_t) ( nodetrack_t * mbr , const char * node , cl_uuid_t u , nodetrack_change_t reason , gpointer user_data); nodetrack_t* nodetrack_new(nodetrack_callback_t callback , gpointer user_data); void nodetrack_del(nodetrack_t*); gboolean nodetrack_nodeup(nodetrack_t* mbr, const char * node , cl_uuid_t u); gboolean nodetrack_nodedown(nodetrack_t* mbr, const char * node , cl_uuid_t u); gboolean nodetrack_ismember(nodetrack_t* mbr, const char * node , cl_uuid_t u); int nodetrack_iterate(nodetrack_t* mbr , nodetrack_iterator_t i, gpointer user_data); /* An intesection nodetrack table * A node is put into the "intersection" nodetrack_t table when it is in all * the underlying constituent nodetrack_t tables, and removed when it is * removed from any of them. * Note that you can set a callback to be informed when these "intersection" * membership changes occur. */ nodetrack_intersection_t* nodetrack_intersection_new(nodetrack_t** tables, int ntables , nodetrack_callback_t callback, gpointer user_data); void nodetrack_intersection_del(nodetrack_intersection_t*); nodetrack_t* nodetrack_intersection_table(nodetrack_intersection_t*); #if 0 /* * I don't know if this should be in this library, or just in * the CCM. Probably only the CCM _should_ be using it (when I write it) */ /* * Use of the nodetrack_hb_* functions implies you're using the heartbeat * peer-connectivity information as your source of information. This is * really only suitable if you're using heartbeat's low-level group membership * for your source of who to expect replies from. * If you're using nodetrack_hb_init, this replaces step (1) above. */ void nodetrack_hb_init(void) /* * If you're using nodetrack_hb_statusmsg, just pass it all status messages * and all peer-connectivity status messages or even all heartbeat messages * (non-status messages will be ignored). * This replaces steps (2) and (3) above _if_ you're using heartbeat low * level membership for your source of who to expect replies from. */ void nodetrack_hb_statusmsg(struct ha_msg* statusmsg); #endif /*0*/ #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/setproctitle.h0000644000000000000000000000476112120057602027742 0ustar00usergroup00000000000000/* * setproctitle.h * * The code in this file, setproctitle.h is heavily based on code from * proftpd, please see the licening information below. * * This file added to the heartbeat tree by Horms * * Code to portably change the title of a programme as displayed * by ps(1). * * heartbeat: Linux-HA heartbeat code * * Copyright (C) 1999,2000,2001 Alan Robertson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu * and other respective copyright holders give permission to link this program * with OpenSSL, and distribute the resulting executable, without including * the source code for OpenSSL in the source distribution. */ #ifndef _HA_SETPROCTITLE_H #define _HA_SETPROCTITLE_H #include int init_set_proc_title(int argc, char *argv[], char *envp[]); void set_proc_title(const char *fmt,...) G_GNUC_PRINTF(1,2); #endif /* _HA_SETPROCTITLE_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/timers.h0000644000000000000000000000162512120057602026520 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLPLUMBING_TIMERS_H # define _CLPLUMBING_TIMERS_H int setmsrepeattimer(long ms); int setmsalarm(long ms); int cancelmstimer(void); long mssleep(long ms); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/clplumbing/uids.h0000644000000000000000000000223212120057602026154 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CLPLUMBING_UIDS_H # define CLPLUMBING_UIDS_H #include /* Tell us who you want to be - or zero for nobody */ int drop_privs(uid_t uid, gid_t gid); /* Return to original privileged state */ int return_to_orig_privs(void); /* Drop down to (probably nobody) privileges again */ int return_to_dropped_privs(void); /* Return TRUE if we have full privileges at the moment */ int cl_have_full_privs(void); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/compress.h0000644000000000000000000000343412120057602024714 0ustar00usergroup00000000000000/* * compress.h: Compression functions for Linux-HA * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _COMPRESS_H_ #define _COMPRESS_H_ #define HB_COMPRESS_TYPE compress #define HB_COMPRESS_TYPE_S "compress" /* * List of functions provided by implementations of the heartbeat * compress interface. */ struct hb_compress_fns { int (*compress) (char*, size_t*, const char*, size_t); int (*decompress) (char*, size_t* , const char*, size_t); const char* (*getname) (void); }; struct ha_msg; /* set the compression method*/ int cl_compress_remove_plugin(const char* pluginname); int cl_compress_load_plugin(const char* pluginname); struct hb_compress_fns* cl_get_compress_fns(void); int cl_set_compress_fns(const char*); char* cl_compressmsg(struct ha_msg*m, size_t* len); struct ha_msg* cl_decompressmsg(struct ha_msg* m); gboolean is_compressed_msg(struct ha_msg* m); int cl_compress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen); int cl_decompress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen); #endif Reusable-Cluster-Components-glue--3cff550e1084/include/glue_config.h.in0000644000000000000000000000367112120057602025752 0ustar00usergroup00000000000000/* include/config.h.in. Generated from configure.in by autoheader. */ /* Location for daemons */ #undef GLUE_DAEMON_DIR /* Group to run daemons as */ #undef GLUE_DAEMON_GROUP /* User to run daemons as */ #undef GLUE_DAEMON_USER /* Where to keep state files and sockets */ #undef GLUE_STATE_DIR /* Location of shared data */ #undef GLUE_SHARED_DIR /* User to run daemons as */ #undef HA_CCMUSER /* Group to run daemons as */ #undef HA_APIGROUP /* Location for daemons */ #undef HA_LIBHBDIR /* top directory of area to drop core files in */ #undef HA_COREDIR /* top directory for LRM related files */ #undef LRM_VARLIBDIR /* CIB secrets */ #undef LRM_CIBSECRETS /* Logging Daemon IPC socket name */ #undef HA_LOGDAEMON_IPC /* Default logging facility */ #undef HA_LOG_FACILITY /* Default plugin search path */ #undef PILS_BASE_PLUGINDIR /* Where to find plugins */ #undef HA_PLUGIN_DIR /* Location of system configuration files */ #undef HA_SYSCONFDIR /* Web site base URL */ #undef HA_URLBASE /* Whatever this used to mean */ #undef HA_VARLIBHBDIR #undef HA_VARLIBDIR /* System lock directory */ #undef HA_VARLOCKDIR /* Where Heartbeat keeps state files and sockets - old name */ #undef HA_VARRUNDIR /* Location for v1 Heartbeat RAs */ #undef HB_RA_DIR /* Where to find LRM plugins */ #undef LRM_PLUGIN_DIR /* Location for LSB RAs */ #undef LSB_RA_DIR /* Location for OCF RAs */ #undef OCF_RA_DIR /* OCF root directory - specified by the OCF standard */ #undef OCF_ROOT_DIR /* Compiling for Darwin platform */ #undef ON_DARWIN /* Compiling for Linux platform */ #undef ON_LINUX /* Current glue version */ #undef GLUE_VERSION /* Build version */ #undef GLUE_BUILD_VERSION /* Location of non-pluing stonith scripts */ #undef STONITH_EXT_PLUGINDIR /* Location of RHCS stonith scripts */ #undef STONITH_RHCS_PLUGINDIR /* Location of stonith plugins */ #undef STONITH_MODULES /* Stonith plugin domain */ #undef ST_TEXTDOMAIN #undef HA_HBCONF_DIR Reusable-Cluster-Components-glue--3cff550e1084/include/ha_msg.h0000644000000000000000000004154412120057602024323 0ustar00usergroup00000000000000/* * Intracluster message object (struct ha_msg) * * Copyright (C) 1999, 2000 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _HA_MSG_H # define _HA_MSG_H 1 #include #include #include #include #include #include #include #include #include enum cl_netstring_type{ FT_STRING = 0, FT_BINARY, FT_STRUCT, FT_LIST, FT_COMPRESS, FT_UNCOMPRESS }; enum cl_msgfmt{ MSGFMT_NVPAIR, MSGFMT_NETSTRING }; #define NEEDHEAD 1 #define NOHEAD 0 #define HA_MSG_ASSERT(X) do{ if(!(X)){ \ cl_log(LOG_ERR, "Assertion failed on line %d in file \"%s\"" \ , __LINE__, __FILE__); \ abort(); \ } \ }while(0) typedef struct hb_msg_stats_s { unsigned long totalmsgs; /* Total # of messages */ /* ever handled */ unsigned long allocmsgs; /* # Msgs currently allocated */ longclock_t lastmsg; }hb_msg_stats_t; struct ha_msg { int nfields; int nalloc; char ** names; size_t* nlens; void ** values; size_t* vlens; int * types; }; typedef struct ha_msg HA_Message; struct fieldtypefuncs_s{ /* memfree frees the memory involved*/ void (*memfree)(void*); /* dup makes a complete copy of the field*/ void* (*dup)(const void*, size_t); /* display printout the field*/ void (*display)(int, int, char* , void*, int); /* add the field into a message*/ int (*addfield) (struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth); /* return the string length required to add this field*/ int (*stringlen) (size_t namlen, size_t vallen, const void* value); /* return the netstring length required to add this field*/ int (*netstringlen) (size_t namlen, size_t vallen, const void* value); /* print the field into the provided buffer, convert it first */ /* if ncecessary*/ int (*tostring)(char*, char*, void* ,size_t,int); /* print the field into the provided buffer*/ int (*tonetstring)(char*, char*, char*, size_t, void*, size_t, int, size_t*); /* convert the given string to a field note: this functions involves allocate memory for for the field */ int (*stringtofield)(void*, size_t, int depth, void**, size_t* ); /* convert the given netstring to a field note: this functions involves allocate memory for for the field */ int (*netstringtofield)(const void*, size_t, void**, size_t*); /* action before packing*/ int (*prepackaction)(struct ha_msg* m, int index); /* action before a user get the value of a field*/ int (*pregetaction)(struct ha_msg* m, int index); }; #define NUM_MSG_TYPES 6 extern struct fieldtypefuncs_s fieldtypefuncs[NUM_MSG_TYPES]; #define MSG_NEEDAUTH 0x01 #define MSG_ALLOWINTR 0X02 #define MSG_NEEDCOMPRESS 0x04 #define MSG_NOSIZECHECK 0x08 #define IFACE "!^!\n" #define MSG_START ">>>\n" #define MSG_END "<<<\n" #define MSG_START_NETSTRING "###\n" #define MSG_END_NETSTRING "%%%\n" #define EQUAL "=" #define MAXDEPTH 16 /* Maximum recursive message depth */ #define MAXLENGTH 1024 /* Common field names for our messages */ #define F_TYPE "t" /* Message type */ #define F_SUBTYPE "subt" /* Message type */ #define F_ORIG "src" /* Real Originator */ #define F_ORIGUUID "srcuuid" /* Real Originator uuid*/ #define F_NODE "node" /* Node being described */ #define F_NODELIST "nodelist" /* Node list being described */ #define F_DELNODELIST "delnodelist" /* Del node list being described */ #define F_TO "dest" /* Destination (optional) */ #define F_TOUUID "destuuid" /* Destination uuid(optional) */ #define F_STATUS "st" /* New status (type = status) */ #define F_WEIGHT "weight" /* weight of node */ #define F_SITE "site" /* site of node */ #define F_PROTOCOL "protocol" /* Protocol number for communication*/ #define F_CLIENTNAME "cn" /* Client name */ #define F_CLIENTSTATUS "cs" /* Client status */ #define F_TIME "ts" /* Timestamp */ #define F_SEQ "seq" /* Sequence number */ #define F_LOAD "ld" /* Load average */ #define F_COMMENT "info" /* Comment */ #define F_TTL "ttl" /* Time To Live */ #define F_AUTH "auth" /* Authentication string */ #define F_HBGENERATION "hg" /* Heartbeat generation number */ #define F_CLIENT_GENERATION "client_gen" /* client generation number*/ #define F_FIRSTSEQ "firstseq" /* Lowest seq # to retransmit */ #define F_LASTSEQ "lastseq" /* Highest seq # to retransmit */ #define F_RESOURCES "rsc_hold" /* What resources do we hold? */ #define F_FROMID "from_id" /* from Client id */ #define F_TOID "to_id" /* To client id */ #define F_PID "pid" /* PID of client */ #define F_UID "uid" /* uid of client */ #define F_GID "gid" /* gid of client */ #define F_ISSTABLE "isstable" /* true/false for RESOURCES */ #define F_APIREQ "reqtype" /* API request type for "hbapi" */ #define F_APIRESULT "result" /* API request result code */ #define F_IFNAME "ifname" /* Interface name */ #define F_PNAME "pname" /* Parameter name */ #define F_PVALUE "pvalue" /* Parameter name */ #define F_DEADTIME "deadtime" /* Dead time interval in ms. */ #define F_KEEPALIVE "keepalive" /* Keep alive time interval in ms. */ #define F_LOGFACILITY "logfacility" /* Suggested cluster syslog facility */ #define F_NODETYPE "nodetype" /* Type of node */ #define F_NUMNODES "numnodes" /* num of total nodes(excluding ping nodes*/ #define F_RTYPE "rtype" /* Resource type */ #define F_ORDERSEQ "oseq" /* Order Sequence number */ #define F_DT "dt" /* Dead time field for heartbeat*/ #define F_ACKSEQ "ackseq" /* The seq number this msg is acking*/ #define F_CRM_DATA "crm_xml" #define F_XML_TAGNAME "__name__" #define F_STATE "state" /*used in ccm for state info*/ /* Message types */ #define T_STATUS "status" /* Status (heartbeat) */ #define T_IFSTATUS "ifstat" /* Interface status */ #define T_ASKRESOURCES "ask_resources" /* Let other node ask my resources */ #define T_ASKRELEASE "ip-request" /* Please give up these resources... */ #define T_ACKRELEASE "ip-request-resp"/* Resources given up... */ #define T_QCSTATUS "query-cstatus" /* Query client status */ #define T_RCSTATUS "respond-cstatus"/* Respond client status */ #define T_STONITH "stonith" /* Stonith return code */ #define T_SHUTDONE "shutdone" /* External Shutdown complete */ #define T_CRM "crmd" /* Cluster resource manager message */ #define T_ATTRD "attrd" /* Cluster resource manager message */ #define T_ADDNODE "addnode" /* Add node message*/ #define T_DELNODE "delnode" /* Delete node message*/ #define T_SETWEIGHT "setweight" /* Set node weight*/ #define T_SETSITE "setsite" /* Set node site*/ #define T_REQNODES "reqnodes" /* Request node list */ #define T_REPNODES "repnodes" /* reply node list rquest*/ #define T_APIREQ "hbapi-req" /* Heartbeat API request */ #define T_APIRESP "hbapi-resp" /* Heartbeat API response */ #define T_APICLISTAT "hbapi-clstat" /* Client status notification" */ #define NOSEQ_PREFIX "NS_" /* PREFIX: Give no sequence number */ /* Used for messages which can't be retransmitted */ /* Either they're protocol messages or from dumb (ping) endpoints */ #define T_REXMIT NOSEQ_PREFIX "rexmit" /* Rexmit request */ #define T_NAKREXMIT NOSEQ_PREFIX "nak_rexmit" /* NAK Rexmit request */ #define T_NS_STATUS NOSEQ_PREFIX "st" /* ping status */ #define T_ACKMSG NOSEQ_PREFIX "ackmsg" /* ACK message*/ /* Messages associated with nice_failback */ #define T_STARTING "starting" /* Starting Heartbeat */ /* (requesting resource report) */ #define T_RESOURCES "resource" /* Resources report */ /* Messages associated with stonith completion results */ #define T_STONITH_OK "OK" /* stonith completed successfully */ #define T_STONITH_BADHOST "badhost" /* stonith failed */ #define T_STONITH_BAD "bad" /* stonith failed */ #define T_STONITH_NOTCONFGD "n_stnth" /* no stonith device configured */ #define T_STONITH_UNNEEDED "unneeded" /* STONITH not required */ /* Set up message statistics area */ int netstring_extra(int); int cl_msg_stats_add(longclock_t time, int size); void cl_msg_setstats(volatile hb_msg_stats_t* stats); void cl_dump_msgstats(void); void cl_set_compression_threshold(size_t threadhold); void cl_set_traditional_compression(gboolean value); /* Allocate new (empty) message */ struct ha_msg * ha_msg_new(int nfields); /* Free message */ void ha_msg_del(struct ha_msg *msg); /* Copy message */ struct ha_msg* ha_msg_copy(const struct ha_msg *msg); int ha_msg_expand(struct ha_msg* msg ); /*Add a null-terminated name and binary value to a message*/ int ha_msg_addbin(struct ha_msg * msg, const char * name, const void * value, size_t vallen); int ha_msg_adduuid(struct ha_msg * msg, const char * name, const cl_uuid_t* uuid); /* Add null-terminated name and a value to the message */ int ha_msg_add(struct ha_msg * msg , const char* name, const char* value); int cl_msg_remove(struct ha_msg* msg, const char* name); int cl_msg_remove_value(struct ha_msg* msg, const void* value); int cl_msg_remove_offset(struct ha_msg* msg, int offset); /* Modify null-terminated name and a value to the message */ int cl_msg_modstring(struct ha_msg * msg, const char* name, const char* value); int cl_msg_modbin(struct ha_msg * msg, const char* name, const void* value, size_t vlen); int cl_msg_moduuid(struct ha_msg * msg, const char * name, const cl_uuid_t* uuid); int cl_msg_modstruct(struct ha_msg * msg, const char* name, const struct ha_msg* value); #define ha_msg_mod(msg, name, value) cl_msg_modstring(msg, name, value) int cl_msg_replace(struct ha_msg* msg, int index, const void* value, size_t vlen, int type); int cl_msg_replace_value(struct ha_msg* msg, const void *old_value, const void* value, size_t vlen, int type); /* Add name, value (with known lengths) to the message */ int ha_msg_nadd(struct ha_msg * msg, const char * name, int namelen , const char * value, int vallen); /* Add a name/value/type to a message (with sizes for name and value) */ int ha_msg_nadd_type(struct ha_msg * msg, const char * name, int namelen , const char * value, int vallen, int type); /* Add name=value string to a message */ int ha_msg_add_nv(struct ha_msg* msg, const char * nvline, const char * bufmax); /* Return value associated with particular name */ #define ha_msg_value(m,name) cl_get_string(m, name) /* Call wait(in|out) but only for a finite time */ int cl_ipc_wait_timeout( IPC_Channel * chan, int (*waitfun)(IPC_Channel * chan), unsigned int timeout); /* Reads an IPC stream -- converts it into a message */ struct ha_msg * msgfromIPC_timeout(IPC_Channel *ch, int flag, unsigned int timeout, int *rc_out); struct ha_msg * msgfromIPC(IPC_Channel * f, int flag); IPC_Message * ipcmsgfromIPC(IPC_Channel * ch); /* Reads a stream -- converts it into a message */ struct ha_msg * msgfromstream(FILE * f); /* Reads a stream with string format--converts it into a message */ struct ha_msg * msgfromstream_string(FILE * f); /* Reads a stream with netstring format--converts it into a message */ struct ha_msg * msgfromstream_netstring(FILE * f); /* Same as above plus copying the iface name to "iface" */ struct ha_msg * if_msgfromstream(FILE * f, char *iface); /* Writes a message into a stream */ int msg2stream(struct ha_msg* m, FILE * f); /* Converts a message into a string and adds the iface name on start */ char * msg2if_string(const struct ha_msg *m, const char * iface); /* Converts a string gotten via UDP into a message */ struct ha_msg * string2msg(const char * s, size_t length); /* Converts a message into a string */ char * msg2string(const struct ha_msg *m); /* Converts a message into a string in the provided buffer with certain depth and with or without start/end */ int msg2string_buf(const struct ha_msg *m, char* buf, size_t len, int depth, int needhead); /* Converts a message into wire format */ char* msg2wirefmt(struct ha_msg *m, size_t* ); char* msg2wirefmt_noac(struct ha_msg*m, size_t* len); /* Converts wire format data into a message */ struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag); /* Convets wire format data into an IPC message */ IPC_Message* wirefmt2ipcmsg(void* p, size_t len, IPC_Channel* ch); /* Converts an ha_msg into an IPC message */ IPC_Message* hamsg2ipcmsg(struct ha_msg* m, IPC_Channel* ch); /* Converts an IPC message into an ha_msg */ struct ha_msg* ipcmsg2hamsg(IPC_Message*m); /* Outputs a message to an IPC channel */ int msg2ipcchan(struct ha_msg*m, IPC_Channel*ch); /* Outpus a message to an IPC channel without authencating the message */ struct ha_msg* msgfromIPC_noauth(IPC_Channel * ch); /* Reads from control fifo, and creates a new message from it */ /* This adds the default sequence#, load avg, etc. to the message */ struct ha_msg * controlfifo2msg(FILE * f); /* Check if the message is authenticated */ gboolean isauthentic(const struct ha_msg * msg); /* Get the required string length for the given message */ int get_stringlen(const struct ha_msg *m); /* Get the requried netstring length for the given message*/ int get_netstringlen(const struct ha_msg *m); /* Add a child message to a message as a field */ int ha_msg_addstruct(struct ha_msg * msg, const char * name, const void* ptr); int ha_msg_addstruct_compress(struct ha_msg*, const char*, const void*); /* Get binary data from a message */ const void * cl_get_binary(const struct ha_msg *msg, const char * name, size_t * vallen); /* Get uuid data from a message */ int cl_get_uuid(const struct ha_msg *msg, const char * name, cl_uuid_t* retval); /* Get string data from a message */ const char * cl_get_string(const struct ha_msg *msg, const char *name); /* Get the type for a field from a message */ int cl_get_type(const struct ha_msg *msg, const char *name); /* Get a child message from a message*/ struct ha_msg *cl_get_struct(struct ha_msg *msg, const char* name); /* Log the contents of a message */ void cl_log_message (int log_level, const struct ha_msg *m); /* Supply messaging system with old style authentication/authorization method */ void cl_set_oldmsgauthfunc(gboolean (*authfunc)(const struct ha_msg*)); /* Set default messaging format */ void cl_set_msg_format(enum cl_msgfmt mfmt); /* Add a string to a list*/ int cl_msg_list_add_string(struct ha_msg* msg, const char* name, const char* value); /* Return length of a list*/ int cl_msg_list_length(struct ha_msg* msg, const char* name); /* Return nth element of a list*/ void* cl_msg_list_nth_data(struct ha_msg* msg, const char* name, int n); /* Functions to add/mod/get an integer */ int ha_msg_add_int(struct ha_msg * msg, const char * name, int value); int ha_msg_mod_int(struct ha_msg * msg, const char * name, int value); int ha_msg_value_int(const struct ha_msg * msg, const char * name, int* value); /* Functions to add/mod/get an unsigned long */ int ha_msg_add_ul(struct ha_msg * msg, const char * name, unsigned long value); int ha_msg_mod_ul(struct ha_msg * msg, const char * name, unsigned long value); int ha_msg_value_ul(const struct ha_msg * msg, const char * name, unsigned long* value); /* Functions to add/get a string list*/ GList* ha_msg_value_str_list(struct ha_msg * msg, const char * name); int cl_msg_add_list_int(struct ha_msg* msg, const char* name, int* buf, size_t n); int cl_msg_get_list_int(struct ha_msg* msg, const char* name, int* buf, size_t* n); GList* cl_msg_get_list(struct ha_msg* msg, const char* name); int cl_msg_add_list(struct ha_msg* msg, const char* name, GList* list); int cl_msg_add_list_str(struct ha_msg* msg, const char* name, char** buf, size_t n); /* Function to add/get a string hash table*/ GHashTable* ha_msg_value_str_table(struct ha_msg * msg, const char * name); int ha_msg_add_str_table(struct ha_msg * msg, const char * name, GHashTable* hash_table); int ha_msg_mod_str_table(struct ha_msg * msg, const char * name, GHashTable* hash_table); /*internal use for list type*/ size_t string_list_pack_length(const GList* list); int string_list_pack(GList* list, char* buf, char* maxp); GList* string_list_unpack(const char* packed_str_list, size_t length); void list_cleanup(GList* list); gboolean must_use_netstring(const struct ha_msg*); #endif /* __HA_MSG_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/lha_internal.h0000644000000000000000000001316512120057602025523 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef LHA_INTERNAL_H # define LHA_INTERNAL_H #define EOS '\0' #define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) ) #define STRLEN_CONST(conststr) ((size_t)((sizeof(conststr)/sizeof(char))-1)) #define STRNCMP_CONST(varstr, conststr) strncmp((varstr), conststr, STRLEN_CONST(conststr)+1) #define STRLEN(c) STRLEN_CONST(c) #define MALLOCT(t) ((t *) malloc(sizeof(t))) #define HADEBUGVAL "HA_DEBUG" /* current debug value (if nonzero) */ #define HALOGD "HA_LOGD" /* whether we use logging daemon or not */ /* Needs to be defined before any other includes, otherwise some system * headers do not behave as expected! Major black magic... */ #undef _GNU_SOURCE /* in case it was defined on the command line */ #define _GNU_SOURCE /* Please leave this as the first #include - Solaris needs it there */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef BSD # define SCANSEL_CAST (void *) #else # define SCANSEL_CAST /* Nothing */ #endif #if defined(ANSI_ONLY) && !defined(inline) # define inline /* nothing */ # undef NETSNMP_ENABLE_INLINE # define NETSNMP_NO_INLINE 1 #endif #ifndef HAVE_DAEMON /* We supply a replacement function, but need a prototype */ int daemon(int nochdir, int noclose); #endif /* HAVE_DAEMON */ #ifndef HAVE_SETENV /* We supply a replacement function, but need a prototype */ int setenv(const char *name, const char * value, int why); #endif /* HAVE_SETENV */ #ifndef HAVE_UNSETENV /* We supply a replacement function, but need a prototype */ int unsetenv(const char *name); #endif /* HAVE_UNSETENV */ #ifndef HAVE_STRERROR /* We supply a replacement function, but need a prototype */ char * strerror(int errnum); #endif /* HAVE_STRERROR */ #ifndef HAVE_SCANDIR /* We supply a replacement function, but need a prototype */ # include int scandir (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT /* This is what the Linux man page says */ int (*compare_function) (const struct dirent**, const struct dirent**) #else /* This is what the Linux header file says ... */ int (*compare_function) (const void *, const void *) #endif ); #endif /* HAVE_SCANDIR */ #ifndef HAVE_ALPHASORT # include int alphasort(const void *dirent1, const void *dirent2); #endif /* HAVE_ALPHASORT */ #ifndef HAVE_INET_PTON /* We supply a replacement function, but need a prototype */ int inet_pton(int af, const char *src, void *dst); #endif /* HAVE_INET_PTON */ #ifndef HAVE_STRNLEN size_t strnlen(const char *s, size_t maxlen); #else # define USE_GNU #endif #ifndef HAVE_STRNDUP char *strndup(const char *str, size_t len); #else # define USE_GNU #endif #ifndef HAVE_STRLCPY size_t strlcpy(char * dest, const char *source, size_t len); #endif #ifndef HAVE_STRLCAT size_t strlcat(char * dest, const char *source, size_t len); #endif #ifndef HAVE_NFDS_T typedef unsigned int nfds_t; #endif #ifdef HAVE_STRUCT_UCRED_DARWIN # include # ifndef SYS_NMLN # define SYS_NMLN _SYS_NAMELEN # endif /* SYS_NMLN */ #endif #define POINTER_TO_SIZE_T(p) ((size_t)(p)) /*pointer cast as size_t*/ #define POINTER_TO_SSIZE_T(p) ((ssize_t)(p)) /*pointer cast as ssize_t*/ #define POINTER_TO_ULONG(p) ((unsigned long)(p)) /*pointer cast as unsigned long*/ /* Sometimes we get a const g_something *, but need to pass it internally * to other functions taking a non-const g_something *, which results * with gcc and -Wcast-qual in a compile time warning, and with -Werror * even to a compile time error. * Workarounds have been to e.g. memcpy(&list, _list); or similar, * the reason of which is non-obvious to the casual reader. * This macro achieves the same, and annotates why it is done. */ #define UNCONST_CAST_POINTER(t, p) ((t)(unsigned long)(p)) #define HAURL(url) HA_URLBASE url /* * Some compilers may not have defined __FUNCTION__. */ #ifndef __FUNCTION__ /* Sun studio compiler */ # ifdef __SUNPRO_C # define __FUNCTION__ __func__ # endif /* Similarly add your compiler here ... */ #endif /* You may need to change this for your compiler */ #ifdef HAVE_STRINGIZE # define ASSERT(X) {if(!(X)) ha_assert(#X, __LINE__, __FILE__);} #else # define ASSERT(X) {if(!(X)) ha_assert("X", __LINE__, __FILE__);} #endif /* shamelessly stolen from linux kernel */ /* Force a compilation error if condition is true */ #define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition)) /* Force a compilation error if condition is true, but also produce a * result (of value 0 and type size_t), so the expression can be used * e.g. in a structure initializer (or where-ever else comma expressions * aren't permitted). */ #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) #endif /* LHA_INTERNAL_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/lrm/Makefile.am0000644000000000000000000000164012120057602025533 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in idir=$(includedir)/heartbeat/lrm i_HEADERS = lrm_api.h lrm_msg.h racommon.h raexec.h Reusable-Cluster-Components-glue--3cff550e1084/include/lrm/lrm_api.h0000644000000000000000000003112012120057602025267 0ustar00usergroup00000000000000/* * Client-side Local Resource Manager API. * * Author: Huang Zhen * Copyright (C) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * * By Huang Zhen 2004/2/23 * * It is based on the works of Alan Robertson, Lars Marowsky Bree, * Andrew Beekhof. * * The Local Resource Manager needs to provide the following functionalities: * 1. Provide the information of the resources holding by the node to its * clients, including listing the resources and their status. * 2. Its clients can add new resources to lrm or remove from it. * 3. Its clients can ask lrm to operate the resources, including start, * restart, stop and so on. * 4. Provide the information of the lrm itself, including what types of * resource are supporting by lrm. * * The typical clients of lrm are crm and lrmadmin. */ /* * Notice: * "status" indicates the exit status code of "status" operation * its value is defined in LSB, OCF... * * "state" indicates the state of resource, maybe LRM_RSC_BUSY, LRM_RSC_IDLE * * "op_status" indicates how the op exit. like LRM_OP_DONE,LRM_OP_CANCELLED, * LRM_OP_TIMEOUT,LRM_OP_NOTSUPPORTED. * * "rc" is the return code of an opertioan. it's value is in following enum * which is defined in "raexec.h" * enum UNIFORM_RET_EXECRA { * EXECRA_EXEC_UNKNOWN_ERROR = -2, * EXECRA_NO_RA = -1, * EXECRA_OK = 0, * EXECRA_UNKNOWN_ERROR = 1, * EXECRA_INVALID_PARAM = 2, * EXECRA_UNIMPLEMENT_FEATURE = 3, * EXECRA_INSUFFICIENT_PRIV = 4, * EXECRA_NOT_INSTALLED = 5, * EXECRA_NOT_CONFIGURED = 6, * EXECRA_NOT_RUNNING = 7, * * EXECRA_RA_DEAMON_DEAD1 = 11, * EXECRA_RA_DEAMON_DEAD2 = 12, * EXECRA_RA_DEAMON_STOPPED = 13, * EXECRA_STATUS_UNKNOWN = 14 * }; */ #ifndef __LRM_API_H #define __LRM_API_H 1 #include #include #include #define LRM_PROTOCOL_MAJOR 0 #define LRM_PROTOCOL_MINOR 1 #define LRM_PROTOCOL_VERSION ((LRM_PROTCOL_MAJOR << 16) | LRM_PROTOCOL_MINOR) #define RID_LEN 128 /*lrm's client uses this structure to access the resource*/ typedef struct { char* id; char* type; char* class; char* provider; GHashTable* params; struct rsc_ops* ops; }lrm_rsc_t; /*used in struct lrm_op_t to show how an operation exits*/ typedef enum { LRM_OP_PENDING = -1, LRM_OP_DONE, LRM_OP_CANCELLED, LRM_OP_TIMEOUT, LRM_OP_NOTSUPPORTED, LRM_OP_ERROR }op_status_t; /*for all timeouts: in milliseconds. 0 for no timeout*/ /*this structure is the information of the operation.*/ #define EVERYTIME -1 #define CHANGED -2 /* Notice the interval and target_rc * * when interval==0, the operation will be executed only once * when interval>0, the operation will be executed repeatly with the interval * * when target_rc==EVERYTIME, the client will be notified every time the * operation executed. * when target_rc==CHANGED, the client will be notified when the return code * is different with the return code of last execute of the operation * when target_rc is other value, only when the return code is the same of * target_rc, the client will be notified. */ typedef struct{ /*input fields*/ char* op_type; GHashTable* params; int timeout; char* user_data; int user_data_len; int interval; int start_delay; int copyparams; /* copy parameters to the rsc */ int target_rc; /*output fields*/ op_status_t op_status; int rc; int call_id; char* output; char* rsc_id; char* app_name; char* fail_reason; unsigned long t_run; /* when did the op run (as age) */ unsigned long t_rcchange; /* last rc change (as age) */ unsigned long exec_time; /* time it took the op to run */ unsigned long queue_time; /* time spent in queue */ int rsc_deleted; /* resource just deleted? */ }lrm_op_t; extern const lrm_op_t lrm_zero_op; /* an all-zeroes lrm_op_t value */ lrm_op_t* lrm_op_new(void); void lrm_free_op(lrm_op_t* op); void lrm_free_rsc(lrm_rsc_t* rsc); void lrm_free_str_list(GList* list); void lrm_free_op_list(GList* list); void lrm_free_str_table(GHashTable* table); /*this enum is used in get_cur_state*/ typedef enum { LRM_RSC_IDLE, LRM_RSC_BUSY }state_flag_t; /* defaults for the asynchronous resource failures */ enum { DEFAULT_FAIL_RC = EXECRA_UNKNOWN_ERROR }; #define DEFAULT_FAIL_REASON "asynchronous monitor error" #define ASYNC_OP_NAME "asyncmon" /* in addition to HA_OK and HA_FAIL */ #define HA_RSCBUSY 2 struct rsc_ops { /* *perform_op: Performs the operation on the resource. *Notice: op is the operation which need to pass to RA and done asyn * *op: the structure of the operation. Caller can create the op by * lrm_op_new() and release the op using lrm_free_op() * *return: All operations will be asynchronous. * The call will return the call id or failed code immediately. * The call id will be passed to the callback function * when the operation finished later. */ int (*perform_op) (lrm_rsc_t*, lrm_op_t* op); /* *cancel_op: cancel the operation on the resource. * *callid: the call id returned by perform_op() * *return: HA_OK for success, HA_FAIL for failure op not found * or other failure * NB: the client always gets a notification over callback * even for operations which were idle (of course, if * the request has been accepted for processing) */ int (*cancel_op) (lrm_rsc_t*, int call_id); /* *flush_ops: throw away all operations queued for this resource, * and return them as cancelled. * *return: HA_OK for success, HA_FAIL for failure * NB: op is not flushed unless it is idle; * in that case this call will block */ int (*flush_ops) (lrm_rsc_t*); /* *get_cur_state: * return the current state of the resource * *cur_state: current state of the resource * *return: cur_state should be in LRM_RSC_IDLE or LRM_RSC_BUSY. * and the function returns a list of ops. * the list includes: * 1. last ops for each type (start/stop/etc) from current client * 2. current pending ops * 3. all recurring ops waiting to execute * the list is sorted by the call_id of ops. * client can release the list using lrm_free_op_list() */ GList* (*get_cur_state) (lrm_rsc_t*, state_flag_t* cur_state); /* *get_last_result: * return the last op of given type from current client * *op_type: the given type * *return: the last op. if there is no such op, return NULL. * client can release the op using lrm_free_op() */ lrm_op_t* (*get_last_result)(lrm_rsc_t*, const char *op_type); }; /* *lrm_op_done_callback_t: * this type of callback functions are called when some * asynchronous operation is done. * client can release op by lrm_free_op() */ typedef void (*lrm_op_done_callback_t) (lrm_op_t* op); typedef struct ll_lrm { struct lrm_ops* lrm_ops; }ll_lrm_t; struct lrm_ops { int (*signon) (ll_lrm_t*, const char * app_name); int (*signoff) (ll_lrm_t*); int (*delete) (ll_lrm_t*); int (*set_lrm_callback) (ll_lrm_t*, lrm_op_done_callback_t op_done_callback_func); /* *set_lrmd_param: set lrmd parameter *get_lrmd_param: get lrmd parameter * *return: HA_OK for success, HA_FAIL for failure * NB: currently used only for max_child_count */ int (*set_lrmd_param)(ll_lrm_t*, const char *name, const char *value); char* (*get_lrmd_param)(ll_lrm_t*, const char *name); /* int (*set_parameters)(ll_lrm_t*, const GHashTable* option); GHashTable* (*get_all_parameters)(ll_lrm_t*); char * (*get_parameter)(ll_lrm_t *, const char * paramname); char * (*get_parameter_description)(ll_lrm_t*); */ /* *get_rsc_class_supported: * Returns the resource classes supported. * e.g. ocf, heartbeat,lsb... * *return: a list of the names of supported resource classes. * caller can release the list by lrm_free_str_list(). */ GList* (*get_rsc_class_supported)(ll_lrm_t*); /* *get_rsc_type_supported: * Returns the resource types supported of class rsc_class. * e.g. drdb, apache,IPaddr... * *return: a list of the names of supported resource types. * caller can release the list by lrm_free_str_list(). */ GList* (*get_rsc_type_supported)(ll_lrm_t*, const char* rsc_class); /* *get_rsc_provider_supported: * Returns the provider list of the given resource types * e.g. heartbeat, failsafe... * *rsc_provider: if it is null, the default one will used. * *return: a list of the names of supported resource provider. * caller can release the list by lrm_free_str_list(). */ GList* (*get_rsc_provider_supported)(ll_lrm_t*, const char* rsc_class, const char* rsc_type); /* *get_rsc_type_metadata: * Returns the metadata of the resource type * *rsc_provider: if it is null, the default one will used. * *return: the metadata. use g_free() to free. * */ char* (*get_rsc_type_metadata)(ll_lrm_t*, const char* rsc_class, const char* rsc_type, const char* rsc_provider); /* *get_all_type_metadatas: * Returns all the metadata of the resource type of the class * *return: A GHashtable, the key is the RA type, * the value is the metadata. * Now only default RA's metadata will be returned. * please use lrm_free_str_table() to free the return value. */ GHashTable* (*get_all_type_metadata)(ll_lrm_t*, const char* rsc_class); /* *get_all_rscs: * Returns all resources. * *return: a list of id of resources. * caller can release the list by lrm_free_str_list(). */ GList* (*get_all_rscs)(ll_lrm_t*); /* *get_rsc: Gets one resource pointer by the id * *return: the lrm_rsc_t type pointer, NULL for failure * caller can release the pointer by lrm_free_rsc(). */ lrm_rsc_t* (*get_rsc)(ll_lrm_t*, const char* rsc_id); /* *add_rsc: Adds a new resource to lrm. * lrmd holds nothing when it starts. * crm or lrmadmin should add resources to lrm using * this function. * *rsc_id: An id which sould be generated by client, * 128byte(include '\0') UTF8 string * *class: the class of the resource * *type: the type of the resource. * *rsc_provider: if it is null, the default provider will used. * *params: the parameters for the resource. * *return: HA_OK for success, HA_FAIL for failure */ int (*add_rsc)(ll_lrm_t*, const char* rsc_id, const char* class, const char* type, const char* provider, GHashTable* params); /* *delete_rsc: delete the resource by the rsc_id * *return: HA_OK for success, HA_FAIL for failure * NB: resource removal is delayed until all operations are * removed; the client, however, gets the reply immediately */ int (*delete_rsc)(ll_lrm_t*, const char* rsc_id); /* *fail_rsc: fail a resource * Allow asynchronous monitor failures. Notifies all clients * which have operations defined for the resource. * The fail_rc parameter should be set to one of the OCF * return codes (if non-positive it defaults to * OCF_ERR_GENERIC). The fail_reason parameter should * contain the description of the failure (i.e. "daemon * panicked" or similar). If NULL is passed or empty string, * it defaults to "asynchronous monitor failure". * *return: HA_OK for success, HA_FAIL for failure */ int (*fail_rsc)(ll_lrm_t* lrm, const char* rsc_id, const int fail_rc, const char* fail_reason); /* *ipcchan: Return the IPC channel which can be used for determining * when messages are ready to be read. *return: the IPC Channel */ IPC_Channel* (*ipcchan)(ll_lrm_t*); /* *msgready: Returns TRUE (1) when a message is ready to be read. */ gboolean (*msgready)(ll_lrm_t*); /* *rcvmsg: Cause the next message to be read - activating callbacks for * processing the message. If no callback processes the message * it will be ignored. The message is automatically disposed of. * *return: the count of message was received. */ int (*rcvmsg)(ll_lrm_t*, int blocking); }; /* *ll_lrm_new: * initializes the lrm client library. * *llctype: "lrm" * */ ll_lrm_t* ll_lrm_new(const char * llctype); /* *execra_code2string: * Translate the return code of the operation to string * *code: the rc field in lrm_op_t structure */ const char *execra_code2string(uniform_ret_execra_t code); #endif /* __LRM_API_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/lrm/lrm_msg.h0000644000000000000000000001170012120057602025306 0ustar00usergroup00000000000000/* * Message Define For Local Resource Manager * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * By Huang Zhen 2004/2/23 * */ /* * Notice: *"status" indicates the exit status code of "status" operation * its value is defined in LSB *"state" indicates the state of resource, maybe LRM_RSC_BUSY, LRM_RSC_IDLE *"opstate" indicates how the op exit.like LRM_OP_DONE,LRM_OP_CANCELLED, * LRM_OP_TIMEOUT,LRM_OP_NOTSUPPORTED. */ #ifndef __LRM_MSG_H #define __LRM_MSG_H 1 #include #define LRM_CMDPATH HA_VARRUNDIR"/heartbeat/lrm_cmd_sock" #define LRM_CALLBACKPATH HA_VARRUNDIR"/heartbeat/lrm_callback_sock" /*define the field type used by lrm*/ #define F_LRM_TYPE "lrm_t" #define F_LRM_APP "lrm_app" #define F_LRM_PID "lrm_pid" #define F_LRM_UID "lrm_uid" #define F_LRM_GID "lrm_gid" #define F_LRM_RID "lrm_rid" #define F_LRM_RTYPE "lrm_rtype" #define F_LRM_RTYPES "lrm_rtypes" #define F_LRM_RCLASS "lrm_rclass" #define F_LRM_RPROVIDER "lrm_rprovider" #define F_LRM_RPROVIDERS "lrm_rproviders" #define F_LRM_PARAM "lrm_param" #define F_LRM_COPYPARAMS "lrm_copyparams" #define F_LRM_TIMEOUT "lrm_timeout" #define F_LRM_OP "lrm_op" #define F_LRM_OPCNT "lrm_opcount" #define F_LRM_OPSTATUS "lrm_opstatus" #define F_LRM_RC "lrm_rc" #define F_LRM_RET "lrm_ret" #define F_LRM_CALLID "lrm_callid" #define F_LRM_RCOUNT "lrm_rcount" #define F_LRM_RIDS "lrm_rids" #define F_LRM_DATALEN "lrm_datalen" #define F_LRM_DATA "lrm_data" #define F_LRM_STATE "lrm_state" #define F_LRM_INTERVAL "lrm_interval" #define F_LRM_TARGETRC "lrm_targetrc" #define F_LRM_LASTRC "lrm_lastrc" #define F_LRM_STATUS "lrm_status" #define F_LRM_RSCDELETED "lrm_rscdeleted" #define F_LRM_METADATA "lrm_metadata" #define F_LRM_USERDATA "lrm_userdata" #define F_LRM_DELAY "lrm_delay" #define F_LRM_T_RUN "lrm_t_run" #define F_LRM_T_RCCHANGE "lrm_t_rcchange" #define F_LRM_EXEC_TIME "lrm_exec_time" #define F_LRM_QUEUE_TIME "lrm_queue_time" #define F_LRM_FAIL_REASON "lrm_fail_reason" #define F_LRM_ASYNCMON_RC "lrm_asyncmon_rc" #define F_LRM_LRMD_PARAM_NAME "lrm_lrmd_param_name" #define F_LRM_LRMD_PARAM_VAL "lrm_lrmd_param_val" #define PRINT printf("file:%s,line:%d\n",__FILE__,__LINE__); /*define the message typs between lrmd and client lib*/ #define REGISTER "reg" #define GETRSCCLASSES "rclasses" #define GETRSCTYPES "rtypes" #define GETPROVIDERS "rproviders" #define GETRSCMETA "rmetadata" #define GETALLRCSES "getall" #define GETRSC "getrsc" #define GETLASTOP "getlastop" #define GETRSCSTATE "getstate" #define SETMONITOR "setmon" #define GETMONITORS "getmons" #define FLUSHRSC "flush" #define ADDRSC "addrsc" #define DELRSC "delrsc" #define FAILRSC "failrsc" #define PERFORMOP "op" #define ISOPSUPPORT "opspt" #define OPDONE "opdone" #define MONITOR "monitor" #define RETURN "return" #define FLUSHOPS "flushops" #define CANCELOP "cancelop" #define SETLRMDPARAM "setparam" #define GETLRMDPARAM "getparam" #define MAX_INT_LEN 64 #define MAX_NAME_LEN 255 #define MAX_VALUE_LEN 255 #define MAX_PARAM_LEN 1024 GHashTable* copy_str_table(GHashTable* hash_table); GHashTable* merge_str_tables(GHashTable* old, GHashTable* new); void free_str_table(GHashTable* hash_table); /* * message for no parameter, like unreg,types,getall * they do not include any paramters */ struct ha_msg* create_lrm_msg(const char* msg); /* * message for only one parameter - resource id, * like getrsc,delrsc,flush,getstate,getmons */ struct ha_msg* create_lrm_rsc_msg(const char* rid, const char* msg); /* register client message */ struct ha_msg* create_lrm_reg_msg(const char* app_name); /* * add new resource * according to the opinion of Lars, it is awkward that we combine all * parameters in to one string. I think so too. So this call may changed soon */ struct ha_msg* create_lrm_addrsc_msg(const char* rid, const char* class, const char* type, const char* provider, GHashTable* parameter); /* * *the return message from lrmd for reg,unreg,addrsc,delrsc,isopsupport. *these return messages only include return code. * */ struct ha_msg* create_lrm_ret(int rc, int fields); /* * the return message for a status change monitoring. */ struct ha_msg* create_rsc_perform_op_msg (const char* rid, lrm_op_t* op); #endif /* __LRM_MSG_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/lrm/racommon.h0000644000000000000000000000222212120057602025460 0ustar00usergroup00000000000000/* * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RACOMMON_H #define RACOMMON_H void get_ra_pathname(const char* class_path, const char* type, const char* provider, char pathname[]); gboolean filtered(char * file_name); int get_runnable_list(const char* class_path, GList ** rsc_info); int get_failed_exec_rc(void); void closefiles(void); #endif /* RACOMMON_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/lrm/raexec.h0000644000000000000000000001114512120057602025120 0ustar00usergroup00000000000000/* * raexec.h: The universal interface of RA Execution Plugin * * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RAEXEC_H #define RAEXEC_H #include #include /* Uniform return value of executing RA */ enum UNIFORM_RET_EXECRA { EXECRA_EXEC_UNKNOWN_ERROR = -2, EXECRA_NO_RA = -1, EXECRA_OK = 0, EXECRA_UNKNOWN_ERROR = 1, EXECRA_INVALID_PARAM = 2, EXECRA_UNIMPLEMENT_FEATURE = 3, EXECRA_INSUFFICIENT_PRIV = 4, EXECRA_NOT_INSTALLED = 5, EXECRA_NOT_CONFIGURED = 6, EXECRA_NOT_RUNNING = 7, EXECRA_RUNNING_MASTER = 8, EXECRA_FAILED_MASTER = 9, /* For status command only */ EXECRA_RA_DEAMON_DEAD1 = 11, EXECRA_RA_DEAMON_DEAD2 = 12, EXECRA_RA_DEAMON_STOPPED = 13, EXECRA_STATUS_UNKNOWN = 14 }; typedef enum UNIFORM_RET_EXECRA uniform_ret_execra_t; #define RA_MAX_NAME_LENGTH 240 #define RA_MAX_DIRNAME_LENGTH 200 #define RA_MAX_BASENAME_LENGTH 40 /* * RA Execution Interfaces * The plugin usage is divided into two step. First to send out a command to * execute a resource agent via calling function execra. Execra is a unblock * function, always return at once. Then to call function post_query_result to * get the RA exection result. */ struct RAExecOps { /* * Description: * Launch a exection of a resource agent -- normally is a script * * Parameters: * rsc_id: The resource instance id. * rsc_type: The basename of a RA. * op_type: The operation that hope RA to do, such as "start", * "stop" and so on. * cmd_params: The command line parameters need to be passed to * the RA for a execution. * env_params: The enviroment parameters need to be set for * affecting the action of a RA execution. As for * OCF style RA, it's the only way to pass * parameter to the RA. * * Return Value: * 0: RA execution is ok, while the exec_key is a valid value. * -1: The RA don't exist. * -2: There are invalid command line parameters. * -3: Other unkown error when launching the execution. */ int (*execra)( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); /* * Description: * Map the specific ret value to a uniform value. * * Parameters: * ret_execra: the RA type specific ret value. * op_type: the operation type * std_output: the output which the RA write to stdout. * * Return Value: * A uniform value without regarding RA type. */ uniform_ret_execra_t (*map_ra_retvalue)( int ret_execra , const char * op_type , const char * std_output); /* * Description: * List all resource info of this class * * Parameters: * rsc_info: a GList which item data type is rsc_info_t as * defined above, containing all resource info of * this class in the local machine. * * Return Value: * >=0 : succeed. the RA type number of this RA class * -1: failed due to invalid RA directory such as not existing. * -2: failed due to other factors */ int (*get_resource_list)(GList ** rsc_info); /* * Description: * List all providers of this type * * Parameters: * providers: a GList which item data type is string. * the name of providers of the resource agent * * Return Value: * >=0 : succeed. the provider number of this RA * -1: failed due to invalid RA directory such as not existing. * -2: failed due to other factors */ int (*get_provider_list)(const char* ra_type, GList ** providers); /* * Description: * List the metadata of the resource agent this class * * Parameters: * rsc_type: the type of the ra * * Return Value: * !NULL : succeed. the RA metadata. * NULL: failed */ char* (*get_resource_meta)(const char* rsc_type, const char* provider); }; #define RA_EXEC_TYPE RAExec #define RA_EXEC_TYPE_S "RAExec" #endif /* RAEXEC_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/pils/Makefile.am0000644000000000000000000000166612120057602025720 0ustar00usergroup00000000000000# # linux-ha: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # This instance created by Horms # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in idir=$(includedir)/pils i_HEADERS = generic.h interface.h plugin.h Reusable-Cluster-Components-glue--3cff550e1084/include/pils/generic.h0000644000000000000000000001042212120057602025437 0ustar00usergroup00000000000000#ifndef PILS_GENERIC_H #define PILS_GENERIC_H /* * Copyright (C) 2000 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Generic interface (implementation) manager * * This manager will manage any number of types of interfaces. * * This means that when any implementations of our client interfaces register * or unregister, it is us that makes their interfaces show up in the outside * world. * * And, of course, we have to do this in a very generic way, since we have * no idea about the client programs or interface types, or anything else. * * We do that by getting a parameter passed to us which tell us the names * of the interface types we want to manage, and the address of a GHashTable * for each type that we put the implementation in when they register * themselves. * * So, each type of interface that we manage gets its own private * GHashTable of the implementations of that type that are currently * registered. * * For example, if we manage communication modules, their exported * interfaces will be registered in a hash table. If we manage * authentication modules, they'll have their (separate) hash table that * their exported interfaces are registered in. * */ #include /* * Header defintions for using the generic interface/implementation * manager plugin. */ /* * Notification types for the callback function. */ typedef enum { PIL_REGISTER, /* Someone has registered an implementation */ PIL_UNREGISTER /* Someone has unregistered an implementation */ }GenericPILCallbackType; /* A user callback for the generic interface manager */ typedef int (*GenericPILCallback) ( GenericPILCallbackType type /* Event type */ , PILPluginUniv* univ /* pointer to plugin universe */ , const char * iftype /* Interface type */ , const char * ifname /* Implementation (interface) name */ , void * userptr /* Whatever you want it to be ;-) */ ); /* * Structures to declare the set of interface types we're managing. */ typedef struct { const char * iftype; /* What type of interface is this? */ GHashTable** ifmap; /* Table with implementation info */ void* importfuns; /* Functions for interface to import */ GenericPILCallback callback; /* Function2call when events occur */ void* userptr; /* Passed to Callback function */ }PILGenericIfMgmtRqst; /* * What does this look like in practice? * * GHashTable* authmodules = NULL; * GHashTable* commmodules = NULL; * PILGenericIfMgmtRqst RegisterRequests[] = * { * {"auth", &authmodules, &authimports, NULL, NULL}, * {"comm", &commmodules, &commimports, NULL, NULL}, * {NULL, NULL, NULL, NULL, NULL} // NULL entry must be here * }; * * PILPlugin* PluginUniverse; * * PluginUniverse = NewPILPlugin("/usr/lib/whatever/plugins"); * * PILLoadPlugin(PluginUniverse, "InterfaceMgr", "generic", &RegisterRequests); * // N. B.: Passing RegisterRequests as an argument is essential * * Then, when you load an auth module, its exported interface gets added * to "authmodules". When you unload an auth module, it gets removed * from authmodules. * * Then, when you load a comm module, its exported interfaces gets added * to "commodules". When you unload a comm module, its exported * interfaces get removed from "commodules" * * If there are simple changes that would be useful for this generic * plugin manager, then "patches are being accepted" :-) * * On the other hand, If you don't like the way this plugin manager works * in a broader way, you're free to write your own - it's just another * plugin ;-) */ #endif Reusable-Cluster-Components-glue--3cff550e1084/include/pils/interface.h0000644000000000000000000001200212120057602025757 0ustar00usergroup00000000000000/* * Copyright (C) 2000 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef PILS_INTERFACE_H # define PILS_INTERFACE_H # ifndef PILS_PLUGIN_H # include # endif /***************************************************************************** * * The most basic interface type is the "IFManager" interface. * Each interface manager registers and deals with interfaces of a given type. * * Such an interface must be loaded before any plugins of it's type can * be loaded. * * In order to register any plugin of type "foo", we must load a interface of * type "Interface" named "foo". This interface then manages the * registration of all interfaces of type foo. * * To bootstrap, we load a interface of type "Interface" named "Interface" * during the initialization of the plugin system. * * IFManagers will be autoloaded if certain conditions are met... * * If a IFManager is to be autoloaded, there must be one interface manager * per file, and the file must be named according to the type of the * interface it implements, and loaded in the directory named PI_IFMANAGER * ("Interface"). * */ /* * I'm unsure exactly which of the following structures * are needed to write a interface, or a interface manager. * We'll get that figured out and scope the defintions accordingly... */ /* * PILInterface (AKA struct PILInterface_s) holds the information * we use to track a single interface manager. */ struct PILInterface_s { unsigned long MagicNum; PILInterfaceType* interfacetype; /* Parent pointer */ char * interfacename; /* malloced interface name */ PILInterface* ifmanager; /* plugin managing us */ void* exports; /* Exported Functions */ /* for this interface */ PILInterfaceFun if_close; /* Interface close operation*/ void* ud_interface; /* per-interface user data */ int refcnt; /* Ref count for plugin */ PILPlugin* loadingpi; /* Plugin that loaded us */ }; /* * PILInterfaceType (AKA struct PILInterfaceType_s) holds the info * we use to track the set of all interfaces of a single kind. */ struct PILInterfaceType_s { unsigned long MagicNum; char* typename; /* Our interface type name */ GHashTable* interfaces; /* The set of interfaces * of our type. The * "values" are all * PILInterface * objects */ void* ud_if_type; /* per-interface-type user data*/ PILInterfaceUniv* universe; /* Pointer to parent (up) */ PILInterface* ifmgr_ref; /* Pointer to our interface manager */ }; /* * PILInterfaceUniv (AKA struct PILInterfaceUniv_s) holds the information * for all interfaces of all types. From our point of view this is * our universe ;-) */ struct PILInterfaceUniv_s{ unsigned long MagicNum; GHashTable* iftypes; /* * Set of Interface Types * The values are all * PILInterfaceType objects */ struct PILPluginUniv_s* piuniv; /* parallel universe of * plugins */ }; #ifdef ENABLE_PLUGIN_MANAGER_PRIVATE /* * From here to the end is specific to interface managers. * This data is only needed by interface managers, and the interface * management system itself. * */ typedef struct PILInterfaceOps_s PILInterfaceOps; /* Interfaces imported by a IFManager interface */ struct PILInterfaceImports_s { /* Return current reference count */ int (*RefCount)(PILInterface * eifinfo); /* Incr/Decr reference count */ int (*ModRefCount)(PILInterface*eifinfo, int plusminus); /* Unregister us as a interface */ void (*ForceUnRegister)(PILInterface *eifinfo); /* For each client */ void (*ForEachClientDel)(PILInterface* manangerif , gboolean(*f)(PILInterface* clientif, void * other) , void* other); }; /* Interfaces exported by an InterfaceManager interface */ struct PILInterfaceOps_s{ /* * These are the interfaces exported by an InterfaceManager to the * interface management infrastructure. These are not imported * by interfaces - only the interface management infrastructure. */ /* RegisterInterface - register this interface */ PIL_rc (*RegisterInterface)(PILInterface* newif , void** imports); PIL_rc (*UnRegisterInterface)(PILInterface*ifinfo); /* Unregister IF*/ /* And destroy PILInterface object */ }; #endif /* ENABLE_PLUGIN_MANAGER_PRIVATE */ #endif /* PILS_INTERFACE_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/pils/plugin.h.in0000644000000000000000000006072612120057602025742 0ustar00usergroup00000000000000/* * Copyright (C) 2000 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef PILS_PLUGIN_H # define PILS_PLUGIN_H # include # include /* Glib headers generate warnings - so we make them go away */ #define time FOOtime #define index FOOindex #include #undef index #undef time /***************************************************************************** * PILS - Universal Plugin and Interface loading system ***************************************************************************** * * An Overview of PILS... * * PILS is fairly general and reasonably interesting plugin loading system. * We manage both plugins and their interfaces * * This plugin / interface management system is quite general, and should be * directly usable by basically any project on any platform on which it runs * - which should be many, since everything is build with automake * and libtool. * * Some terminology... * * There are two basic kinds of objects we deal with here: * * Plugins: dynamically loaded chunks of code which implement one or more * interfaces. The system treats all plugins as the same. * In UNIX, these are dynamically loaded ".so" files. * * Interface: A set of functions which implement a particular capability * (or interface) * Generally interfaces are registered as part of a plugin. * The system treats all interfaces of the same type the same. * It is common to have exactly one interface inside of each plugin. * In this case, the interface name should match the plugin name. * * Each interface implementation exports certain functions for its clients * to use. We refer to these those "Ops". Every interface of the same type * "imports" the same interfaces from its interface manager, * and exports the same "Ops". * * Each interface implementation is provided certain interfaces which it * imports when it from its interface manager when it is registered. * We refer to these as "Imports". Every interface of a given type * imports the same interfaces. * * The story with plugins is a little different... * * Every plugin exports a certain set of interfaces, regardless of what type * of interfaces is implemented by it. These are described in the * PILPluginOps structure. * * Every plugin imports a certain set of interfaces, regardless of what type * of interfaces it may implement. These are described by the * PILPluginImports structure. * * In the function parameters below, the following notation will * sometimes appear: * * (OP) == Output Parameter - a parameter which is modified by the * function being called * * ***************************************************************************** * * The basic structures we maintain about plugins are as follows: * * PILPlugin The data which represents a plugin. * PILPluginType The data common to all plugins of a given type * PILPluginUniv The set of all plugin types in the Universe * (well... at least *this* universe) * * The basic structures we maintain about interfaces are as follows: * PILInterface The data which represents a interface * PILInterfaceType The data which is common to all * interfaces of a given type * PILPluginUniv The set of all interface types in the Universe * (well... at least *this* universe) * * Regarding "Universe"s. It is our intent that a given program can deal * with plugins in more than one universe. This might occur if you have two * independent libraries each of which uses the plugin loading environment * to manage their own independent interface components. There should be * no restriction in creating a program which uses both of these libraries. * At least that's what we hope ;-) * * *************************************************************************** * SOME MORE DETAILS ABOUT PLUGINS... *************************************************************************** * * Going back to more detailed data structures about plugins... * * PILPluginImports The set of standard functions all plugins * import. * This includes: * register_plugin() * unregister_plugin() * register_interface() * unregister_interface() * load_plugin() * log() Preferred logging function * * PILPluginOps The set of standard operations all plugins * export. * This includes: * pluginversion() * pluginname() * getdebuglevel() * setdebuglevel() * close() Prepare for unloading... * * Although we treat plugins pretty much the same, they are still * categorized into "types" - one type per directory. These types * generally correspond to interface types. * * One can only cause a plugin to be loaded - not a interface. But it is * common to assume that loading a plugin named foo of type bar will * cause a interface named foo of type bar to be registered. If one * wants to implement automatic plugin loading in a given interface type, * this assumption is necessary. * * The general way this works is... * * - A request is made to load a particular plugin of a particular type. * * - The plugin is loaded from the appropriate directory for plugins * of that type. * * - The ml_plugin_init() function is called once when the plugin is * loaded. * * The ml_plugin_init() function is passed a vector of functions which * point to functions it can call to register itself, etc. * (it's of type PILPluginImports) * * The ml_plugin_init function then uses this set of imported functions * to register itself and its interfaces. * * The mechanism of registering a interface is largely the same for * every interface. However, the semantics of registering a interfaces * is determined by the interface manager for the particular type of * interface being discussed. * *************************************************************************** * SOME MORE DETAILS ABOUT PLUGINS... *************************************************************************** * * There is only one built in type of interface. That's the Interface * manager interface. * The interface manager for the interface of type "InterfaceMgr", * named "InterfaceMgr" inserts itself into the system in order * to bootstrap things... * * When an attempt is made to register a interface of an unknown type, * then the appropriate Interface manager is loaded automatically. * * The name of an interface manager determines the type of * interface it manages. * * It handles requests for interfaces whose type is the same * as its interface name. If the interface manager's interface name * is foo, then it is the interface manager for all interfaces whose * type is foo. * * Types associated with interfaces of type Interface * * PILInterfaceOps The set of interfaces that every interface * manager exports * PILInterfaceImports The set of interfaces which are supplied to * (imported by) every interface of type * Interface. (that is, every interface * manager). * ***************************************************************************** * * Each plugin has only one entry point which is exported directly, regardless * of what kind of interface(s) it may implement... * * This entrypoint is named ml_plugin_init() {more or less - see below} * * The ml_plugin_init() function is called once when the plugin is loaded. * * * All other function pointers are registered (exported) through parameters * passed to ml_plugin_init() * * It is the purpose of the Ml_plugin_init() to register the plugin, * and all the interfaces which this plugin implements. A pointer to * the registration function is in the parameters which are passed * to ml_plugin_init(). * ***************************************************************************** * * THINGS IN THIS DESIGN WHICH ARE PROBABLY BROKEN... * * It may also be the case that the plugin loading environment needs * to be able to have some kind of user_data passed to it which it can * also pass along to any interface ... * * Maybe this should be handled by a sort of global user_data registration * structure, so globals can be passed to interfaces when they're registered. * * A sort of "user_data" registry. One for each interface type and one * for each interface... Or maybe it could be even more flexible... * * This is all so that these nice pristene, beautiful concepts can come out * and work well in the real world where interfaces need to interact with * some kind of global system view, and with each other... * * Probably need some better way of managing interface versions, etc. * **************************************************************************** */ /* * If you want to use this funky export stuff, then you need to #define * PIL_PLUGINTYPE and PIL_PLUGIN *before* including this file. * * The way to use this stuff is to declare your primary entry point this way: * * This example is for an plugin of type "auth" named "sha1" * * #define PIL_PLUGINTYPE auth * #define PIL_PLUGIN sha1 * #include * * static const char* Ourpluginversion (void); * static const char* Ourpluginname (void); * static int Ourgetdebuglevel(void); * static void Oursetdebuglevel(int); * static void Ourclose (PILPlugin*); * * static struct PILPluginOps our_exported_plugin_operations = * { Ourpluginversion, * , Ourpluginname * , Ourgetdebuglevel * , Oursetdebuglevel * , Ourclose * }; * * static const PILPluginImports* PluginOps; * static PILPlugin* OurPlugin; * * // Our plugin initialization and registration function * // It gets called when the plugin gets loaded. * PIL_rc * PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) * { * PluginOps = imports; * OurPlugin = us; * * // Register ourself as a plugin * / * imports->register_plugin(us, &our_exported_plugin_operations); * * // Register our interfaces * imports->register_interface(us, "interfacetype", "interfacename" * // Be sure and define "OurExports" and OurImports * // above... * , &OurExports * , &OurImports); * // Repeat for all interfaces in this plugin... * * } * * Except for the PIL_PLUGINTYPE and the PIL_PLUGIN definitions, and changing * the names of various static variables and functions, every single plugin is * set up pretty much the same way * */ /* * No doubt there is a fancy preprocessor trick for avoiding these * duplications but I don't have time to figure it out. Patches are * being accepted... */ #define mlINIT_FUNC _pil_plugin_init #define mlINIT_FUNC_STR "_pil_plugin_init" #define PIL_INSERT _LTX_ #define PIL_INSERT_STR "_LTX_" /* * snprintf-style format string for initialization entry point name: * arguments are: (plugintype, pluginname) */ #define PIL_FUNC_FMT "%s" PIL_INSERT_STR "%s" mlINIT_FUNC_STR #ifdef __STDC__ # define EXPORTHELPER1(plugintype, insert, pluginname, function) \ plugintype##insert##pluginname##function #else # define EXPORTHELPER1(plugintype, insert, pluginname, function) \ plugintype/**/insert/**/pluginname/**/function #endif #define EXPORTHELPER2(a, b, c, d) EXPORTHELPER1(a, b, c, d) #define PIL_PLUGIN_INIT \ EXPORTHELPER2(PIL_PLUGINTYPE,PIL_INSERT,PIL_PLUGIN,mlINIT_FUNC) /* * Plugin loading return codes. OK will always be zero. * * There are many ways to fail, but only one kind of success ;-) */ typedef enum { PIL_OK=0, /* Success */ PIL_INVAL=1, /* Invalid Parameters */ PIL_BADTYPE=2, /* Bad plugin/interface type */ PIL_EXIST=3, /* Duplicate Plugin/Interface name */ PIL_OOPS=4, /* Internal Error */ PIL_NOPLUGIN=5 /* No such plugin or Interface */ }PIL_rc; /* Return code from Plugin fns*/ const char * PIL_strerror(PIL_rc rc); typedef struct PILPluginImports_s PILPluginImports; typedef struct PILPluginOps_s PILPluginOps; typedef struct PILPlugin_s PILPlugin; typedef struct PILPluginUniv_s PILPluginUniv; typedef struct PILPluginType_s PILPluginType; typedef struct PILInterface_s PILInterface; typedef struct PILInterfaceImports_s PILInterfaceImports; typedef struct PILInterfaceUniv_s PILInterfaceUniv; typedef struct PILInterfaceType_s PILInterfaceType; typedef PIL_rc(*PILInterfaceFun)(PILInterface*, void* ud_interface); #define PIL_MAGIC_PLUGIN 0xFEEDBEEFUL #define PIL_MAGIC_PLUGINTYPE 0xFEEDCEEFUL #define PIL_MAGIC_PLUGINUNIV 0xFEEDDEEFUL #define PIL_MAGIC_INTERFACE 0xFEEDEEEFUL #define PIL_MAGIC_INTERFACETYPE 0xFEEDFEEFUL #define PIL_MAGIC_INTERFACEUNIV 0xFEED0EEFUL #define IS_PILPLUGIN(s) ((s)->MagicNum == PIL_MAGIC_PLUGIN) #define IS_PILPLUGINTYPE(s) ((s)->MagicNum == PIL_MAGIC_PLUGINTYPE) #define IS_PILPLUGINUNIV(s) ((s)->MagicNum == PIL_MAGIC_PLUGINUNIV) #define IS_PILINTERFACE(s) ((s)->MagicNum == PIL_MAGIC_INTERFACE) #define IS_PILINTERFACETYPE(s) ((s)->MagicNum == PIL_MAGIC_INTERFACETYPE) #define IS_PILINTERFACEUNIV(s) ((s)->MagicNum == PIL_MAGIC_INTERFACEUNIV) /* The type of a Plugin Initialization Function */ typedef PIL_rc (*PILPluginInitFun) (PILPlugin*us , PILPluginImports* imports , void* plugin_user_data); /* * struct PILPluginOps_s (typedef PILPluginOps) defines the set of functions * exported by all plugins... */ struct PILPluginOps_s { const char* (*pluginversion) (void); int (*getdebuglevel) (void); void (*setdebuglevel) (int); const char* (*license) (void); const char* (*licenseurl) (void); void (*close) (PILPlugin*); }; /* * Logging levels for the "standard" log interface. */ typedef enum { PIL_FATAL= 1, /* BOOM! Causes program to stop */ PIL_CRIT = 2, /* Critical -- serious error */ PIL_WARN = 3, /* Warning */ PIL_INFO = 4, /* Informative message */ PIL_DEBUG= 5 /* Debug message */ }PILLogLevel; typedef void (*PILLogFun)(PILLogLevel priority, const char * fmt, ...); /* * The size glib2 type du jour? * (once, this used to be size_t, so this change could break * distributions with older glib2 versions; if so, just add an * #ifelse below) */ #if GLIB_MINOR_VERSION <= 14 typedef gulong glib_size_t; #else typedef gsize glib_size_t; #endif /* * struct PILPluginImports_s (typedef PILPluginImports) defines * the functions and capabilities that every plugin imports when it is loaded. */ struct PILPluginImports_s { PIL_rc (*register_plugin)(PILPlugin* piinfo , const PILPluginOps* commonops); PIL_rc (*unregister_plugin)(PILPlugin* piinfo); /* * A little explanation of the close_func parameter to register_interface * is in order. * * It is an exported operation function, just like the Ops structure. * However, the Ops vector is exported to applications that * are using the interface. Unlike the Ops structure, close_func is * exported only to the interface system, since applications shouldn't * call it directly, but should manage the reference counts for the * interfaces instead. * The generic interface system doesn't have any idea how to call * any functions in the operations vector. So, it's a separate * parameter for two good reasons. */ PIL_rc (*register_interface)(PILPlugin* piinfo , const char * interfacetype /* Type of interface */ , const char * interfacename /* Name of interface */ , void* Ops /* Info (functions) exported by this interface */ /* Function to call to shut down this interface */ , PILInterfaceFun close_func , PILInterface** interfaceid /* Interface id (OP) */ , void** Imports , void* ud_interface); /* interface user data */ PIL_rc (*unregister_interface)(PILInterface* interfaceid); PIL_rc (*load_plugin)(PILPluginUniv* universe , const char * plugintype, const char * pluginname , void* plugin_private); void (*log) (PILLogLevel priority, const char * fmt, ...); gpointer (*alloc)(glib_size_t size); gpointer (*mrealloc)(gpointer space, glib_size_t size); void (*mfree)(gpointer space); char* (*mstrdup)(const char *s); }; /* * Function for logging with the given logging function * The reason why it's here is so we can get printf arg checking * You can't get that when you call a function pointer directly. */ void PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...) G_GNUC_PRINTF(3,4); /* * EXPORTED INTERFACES... */ /* Create a new plugin universe - start the plugin loading system up */ PILPluginUniv* NewPILPluginUniv(const char * baseplugindirectory); /* Change memory allocation functions right after creating universe */ void PilPluginUnivSetMemalloc(PILPluginUniv* , gpointer (*alloc)(glib_size_t size) , gpointer (*mrealloc)(gpointer, glib_size_t size) , void (*mfree)(void* space) , char* (*mstrdup)(const char *s)); void PilPluginUnivSetLog(PILPluginUniv* , void (*log) (PILLogLevel priority, const char * fmt, ...)); /* Delete a plugin universe - shut the plugin loading system down */ /* Best if used carefully ;-) */ void DelPILPluginUniv(PILPluginUniv*); /* Set the debug level for the plugin system itself */ void PILpisysSetDebugLevel (int level); /* Return a list of plugins of the given type */ char ** PILListPlugins(PILPluginUniv* u, const char *plugintype , int* plugincount /*can be NULL*/); /* Free the plugin list returned by PILFreeListPlugins */ void PILFreePluginList(char ** pluginlist); /* Load the requested plugin */ PIL_rc PILLoadPlugin(PILPluginUniv* piuniv , const char * plugintype , const char * pluginname , void * pi_private); /* Return PIL_OK if the given plugin exists */ PIL_rc PILPluginExists(PILPluginUniv* piuniv , const char * plugintype , const char * pluginname); /* Either or both of pitype and piname may be NULL */ void PILSetDebugLevel(PILPluginUniv*u, const char * pitype , const char * piname , int level); /* Neither pitype nor piname may be NULL */ int PILGetDebugLevel(PILPluginUniv* u, const char * pitype , const char * piname); PIL_rc PILIncrIFRefCount(PILPluginUniv* piuniv , const char * interfacetype , const char * interfacename , int plusminus); int PILGetIFRefCount(PILPluginUniv* piuniv , const char * interfacetype , const char * interfacename); void PILLogMemStats(void); /* The plugin/interface type of a interface manager */ #define PI_IFMANAGER "InterfaceMgr" #define PI_IFMANAGER_TYPE InterfaceMgr /* * These functions are standard exported functions for all plugins. */ #define PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \ /* \ * Prototypes for boilerplate functions \ */ \ static const char* Ourpluginversion(void); \ static int GetOurDebugLevel(void); \ static void SetOurDebugLevel(int); \ static const char * ReturnOurLicense(void); \ static const char * ReturnOurLicenseURL(void); #define PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName) \ /* \ * Definitions of boilerplate functions \ */ \ static const char* \ Ourpluginversion(void) \ { return PluginVersion; } \ \ static int DebugName = 0; \ \ static int \ GetOurDebugLevel(void) \ { return DebugName; } \ \ static void \ SetOurDebugLevel(int level) \ { DebugName = level; } \ \ static const char * \ ReturnOurLicense(void) \ { return PIL_PLUGINLICENSE; } \ \ static const char * \ ReturnOurLicenseURL(void) \ { return PIL_PLUGINLICENSEURL; } #define PIL_PLUGIN_BOILERPLATE(PluginVersion, DebugName, CloseName) \ PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \ static void CloseName(PILPlugin*); \ /* \ * Initialize Plugin Exports structure \ */ \ static PILPluginOps OurPIExports = \ { Ourpluginversion \ , GetOurDebugLevel \ , SetOurDebugLevel \ , ReturnOurLicense \ , ReturnOurLicenseURL \ , CloseName \ }; \ PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName) #define PIL_PLUGIN_BOILERPLATE2(PluginVersion, DebugName) \ PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \ /* \ * Initialize Plugin Exports structure \ */ \ static PILPluginOps OurPIExports = \ { Ourpluginversion \ , GetOurDebugLevel \ , SetOurDebugLevel \ , ReturnOurLicense \ , ReturnOurLicenseURL \ , NULL \ }; \ PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName) /* A few sample licenses and URLs. We can easily add to this */ #define LICENSE_GPL "gpl" #define URL_GPL "http://www.fsf.org/licenses/gpl.html" #define LICENSE_LGPL "lgpl" #define URL_LGPL "http://www.fsf.org/licenses/lgpl.html" #define LICENSE_X11 "x11" #define URL_X11 "http://www.x.org/terms.htm" #define LICENSE_PUBDOM "publicdomain" #define URL_PUBDOM "file:///dev/null" #define LICENSE_MODBSD "modbsd" #define URL_MODBSD "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5" #define LICENSE_OLDBSD "origbsd" #define URL_OLDBSD "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#6" #define LICENSE_EXPAT "expat" #define URL_EXPAT "http://www.jclark.com/xml/copying.txt" #define LICENSE_ZLIB "zlib" #define URL_ZLIB "http://www.gzip.org/zlib/zlib_license.html" #define LICENSE_APACHE_10 "apache1_0" #define URL_APACHE_10 "http://www.apache.org/LICENSE-1.0" #define LICENSE_APACHE_11 "apache1_1" #define URL_APACHE_11 "http://www.apache.org/LICENSE-1.1" #define LICENSE_MPL "mpl" #define URL_MPL "http://www.mozilla.org/MPL/MPL-1.1.html" #define LICENSE_PROP "proprietary" #define URL_PROP "" #define LICENSE_IBMPL "ibmpl" #define URL_IBMPL "http://oss.software.ibm.com/developerworks/opensource/license10.html" #ifdef ENABLE_PIL_DEFS_PRIVATE /* Perhaps these should be moved to a different header file */ /* * PILPluginType is the "class" for the basic plugin loading mechanism. * * To enable loading of plugins from a particular plugin type * one calls NewPILPluginType with the plugin type name, the plugin * base directory, and the set of functions to be imported to the plugin. * * * The general idea of these structures is as follows: * * The PILPluginUniv object contains information about all plugins of * all types. * * The PILPluginType object contains information about all the plugins of a * specific type. * * Note: for plugins which implement a single interface, the plugin type name * should be the same as the interface type name. * * For other plugins that implement more than one interface, one of * the interface names should normally match the plugin name. */ /* * struct PILPlugin_s (typedef PILPlugin) is the structure which * represents/defines a plugin, and is used to identify which plugin is * being referred to in various function calls. * * NOTE: It may be the case that this definition should be moved to * another header file - since no one ought to be messing with them anyway ;-) * * I'm not sure that we're putting the right stuff in here, either... */ struct PILPlugin_s { unsigned long MagicNum; char* plugin_name; PILPluginType* plugintype; /* Parent structure */ int refcnt; /* Reference count for this plugin */ lt_dlhandle dlhandle; /* Reference to D.L. object */ PILPluginInitFun dlinitfun; /* Initialization function */ const PILPluginOps* pluginops; /* Exported plugin operations */ void* ud_plugin; /* Plugin-Private data */ /* Other stuff goes here ... (?) */ }; /* * PILPluginType Information about all plugins of a given type. * (i.e., in a given directory) * (AKA struct PILPluginType_s) */ struct PILPluginType_s { unsigned long MagicNum; char * plugintype; PILPluginUniv* piuniv; /* The universe to which we belong */ GHashTable* Plugins; /* Key is plugin type, value is PILPlugin */ char** (*listplugins)(PILPluginType*, int* listlen); }; /* * PILPluginUniv (aka struct PILPluginUniv_s) is the structure which * represents the universe of all PILPluginType objects. * There is one PILPluginType object for each Plugin type. */ struct PILPluginUniv_s { unsigned long MagicNum; char ** rootdirlist; /* key is plugin type, data is PILPluginType* */ GHashTable* PluginTypes; struct PILInterfaceUniv_s*ifuniv; /* Universe of interfaces */ PILPluginImports* imports; }; # endif /* ENABLE_PIL_DEFS_PRIVATE */ #endif /*PILS_PLUGIN_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/replace_uuid.h0000644000000000000000000000365112120057602025523 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * uuid: wrapper declarations. * * heartbeat originally used "uuid" functionality by calling directly, * and only, onto the "e2fsprogs" implementation. * * The run-time usages in the code have since been abstracted, funnelled * through a thin, common interface layer: a Good Thing. * * Similarly, the compile-time usages of "include " are * replaced, being funnelled through a reference to this header file. * * This header file interfaces onto the actual underlying implementation. * In the case of the "e2fsprogs" implementation, it is simply a stepping * stone onto "". As other implementations are accommodated, * so their header requirements can be accommodated here. * * Copyright (C) 2004 David Lee */ #ifndef REPLACE_UUID_H #define REPLACE_UUID_H typedef unsigned char uuid_t[16]; void uuid_clear(uuid_t uu); int uuid_compare(const uuid_t uu1, const uuid_t uu2); void uuid_copy(uuid_t dst, const uuid_t src); void uuid_generate(uuid_t out); void uuid_generate_random(uuid_t out); int uuid_is_null(const uuid_t uu); int uuid_parse(const char *in, uuid_t uu); void uuid_unparse(const uuid_t uu, char *out); #endif /* REPLACE_UUID_H */ Reusable-Cluster-Components-glue--3cff550e1084/include/stonith/Makefile.am0000644000000000000000000000171312120057602026432 0ustar00usergroup00000000000000# # linux-ha: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # This instance created by Horms # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in idir=$(includedir)/stonith i_HEADERS = expect.h stonith.h stonith_plugin.h st_ttylock.h Reusable-Cluster-Components-glue--3cff550e1084/include/stonith/expect.h0000644000000000000000000000361312120057602026040 0ustar00usergroup00000000000000/* * Expect simple tokens. Simple expect infrastructure for STONITH API * * Copyright (c) 2000 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef __EXPECT_H # define __EXPECT_H /* * If we find any of the given tokens in the input stream, * we return it's "toktype", so we can tell which one was * found. * */ struct Etoken { const char * string; /* The token to look for */ int toktype; /* The type to return on match */ int matchto; /* Modified during matches */ }; int ExpectToken(int fd , struct Etoken * toklist /* List of tokens to match against */ /* Final token has NULL string */ , int to_secs /* Timeout value in seconds */ , char * buf /* If non-NULL, then all the text * matched/skipped over by this match */ , int maxline, , int debug); /* debug level */ /* * A handy little routine. It runs the given process * with it's standard output redirected into our *readfd, and * its standard input redirected from our *writefd * * Doing this with all the pipes, etc. required for doing this * is harder than it sounds :-) */ int StartProcess(const char * cmd, int* readfd, int* writefd); #ifndef EOS # define EOS '\0' #endif #endif /*__EXPECT_H*/ Reusable-Cluster-Components-glue--3cff550e1084/include/stonith/st_ttylock.h0000644000000000000000000000164212120057602026747 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __STONITH_ST_TTYLOCK_H # define __STONITH_ST_TTYLOCK_H int st_ttylock(const char *serial_device); int st_ttyunlock(const char *serial_device); #endif /*__STONITH_ST_TTYLOCK_H*/ Reusable-Cluster-Components-glue--3cff550e1084/include/stonith/stonith.h0000644000000000000000000001537612120057602026251 0ustar00usergroup00000000000000/* * S hoot * T he * O ther * N ode * I n * T he * H ead * * Cause the other machine to reboot or die - now. * * We guarantee that when we report that the machine has been * rebooted, then it has been (barring misconfiguration or hardware * errors) * * A machine which we have STONITHed won't do anything more to its * peripherials etc. until it goes through the reboot cycle. */ /* * * Copyright (c) 2000 Alan Robertson * Copyright (c) 2004 International Business Machines, Inc. * * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef __STONITH_H # define __STONITH_H #include #include #define STONITH_VERS 2 /* * Return codes from "Stonith" operations */ #define S_OK 0 /* Machine correctly reset */ #define S_BADCONFIG 1 /* Bad config info given */ #define S_ACCESS 2 /* Can't access STONITH device */ /* (login/passwd problem?) */ #define S_INVAL 3 /* Bad/illegal argument */ #define S_BADHOST 4 /* Bad/illegal host/node name */ #define S_RESETFAIL 5 /* Reset failed */ #define S_TIMEOUT 6 /* Timed out in the dialogues */ #define S_ISOFF 7 /* Can't reboot: Outlet is off */ #define S_OOPS 8 /* Something strange happened */ typedef struct stonith { char * stype; }Stonith; /* An array of StonithNVpairs is terminated by a NULL s_name */ typedef struct { char * s_name; char * s_value; }StonithNVpair; /* * Operation requested by reset_req() */ #define ST_GENERIC_RESET 1 /* Reset the machine any way you can */ #define ST_POWERON 2 /* Power the node on */ #define ST_POWEROFF 3 /* Power the node off */ /* * Type of information requested by the get_info() call */ #define ST_CONF_XML 1 /* XML config info */ #define ST_DEVICEID 2 /* Device Type Identification */ #define ST_DEVICENAME 3 /* Unique Individual Device Identification */ /* (only after stonith_set_config() call) */ #define ST_DEVICEDESCR 4 /* Device Description text */ #define ST_DEVICEURL 5 /* Manufacturer/Device URL */ extern PILPluginUniv *StonithPIsys; char ** stonith_types(void); /* NULL-terminated list */ /* valid until next call of stonith_types() */ Stonith*stonith_new(const char * type); void stonith_delete(Stonith *); const char * const * stonith_get_confignames (Stonith* s); /* static/global return */ /* Return number and list of valid s_names */ const char* /* static/global return - lots of things! */ stonith_get_info (Stonith* s, int infotype); void stonith_set_debug (Stonith* s, int debuglevel); void stonith_set_log (Stonith* s , PILLogFun); int stonith_set_config (Stonith* s, StonithNVpair* list); int stonith_set_config_file(Stonith* s, const char * configname); /* uses get_confignames to determine which * names to look for in file configname, which * is passed in by the -F option */ int stonith_set_config_info(Stonith* s, const char * info); /* uses get_confignames to determine which * names to look for in string info, which * is passed in by the -p option */ /* * Must call stonith_set_config() before calling functions below... */ char** stonith_get_hostlist (Stonith* s); void stonith_free_hostlist (char** hostlist); int stonith_get_status (Stonith* s); int stonith_req_reset (Stonith* s, int operation, const char* node); StonithNVpair* stonith_env_to_NVpair(Stonith* s); /* Stonith 1 compatibility: Convert string to an NVpair set */ StonithNVpair* stonith1_compat_string_to_NVpair(Stonith* s, const char * str); StonithNVpair* stonith_ghash_to_NVpair(GHashTable* stringtable); void free_NVpair(StonithNVpair*); /* Free result from above 2 functions */ /* * The ST_DEVICEID info call is intended to return the type of the Stonith * device. Note that it may return a different result once it has attempted * to talk to the device (like after a status() call). This is because * a given STONITH module may be able to talk to more than one kind of * model of STONITH device, and can't tell which type is out there * to until it talks to it. For example, Baytech 3, Baytech 5 and * Baytech 5a are all supported by one module, and this module actually * captures the particular model number after it talks to it. * * The ST_DEVICEDESCR info call is intended to return information identifying * the type of STONITH device supported by this STONITH object. This is so * users can tell if they have this kind of device or not. * * SHOULD THIS BE IN THE XML SO IT CAN BE SUPPLIED IN SEVERAL LANGUAGES?? * But, this would mean the STONITH command would have to parse XML. * Sigh... I'd rather not... Or maybe it can be supplied duplicately * in the XML if that is thought to be desirable... * * The ST_DEVICEURL info call is intended to return the URL of a web site * related to the device in question. This might be the manufacturer, * a pointer to the product line, or the individual product itself. * * A good way for a GUI to work which configures STONITH devices would be to * use the result of the stonith_types() call in a pulldown menu. * * Once the type is selected, create a Stonith object of the selected type. * One can then create a dialog box to create the configuration info for the * device using return from the ST_CONF_XML info call to direct the * GUI in what information to ask for to fill up the StonithNVpair * argument to the stonith_set_config() call. This information would then * be prompted for according to the XML information, and then put into * a NULL-terminated array of StonithNVpair objects. * * Once this has been done, it can be tested for syntactic * validity with stonith_set_config(). * * If it passes set_config(), it can be further validated using status() * which will then actually try and talk to the STONITH device. If status() * returns S_OK, then communication with the device was successfully * established. * * Normally that would mean that logins, passwords, device names, and IP * addresses, etc. have been validated as required by the particular device. * * At this point, you can ask the device which machines it knows how to reset * using the stonith_get_hostlist() function. * */ #endif /*__STONITH_H*/ Reusable-Cluster-Components-glue--3cff550e1084/include/stonith/stonith_plugin.h0000644000000000000000000000705512120057602027622 0ustar00usergroup00000000000000/* * S hoot * T he * O ther * N ode * I n * T he * H ead * * Cause the other machine to reboot or die - now. * * We guarantee that when we report that the machine has been * rebooted, then it has been (barring misconfiguration or hardware errors) * * A machine which we have STONITHed won't do anything more to its * peripherials etc. until it goes through the reboot cycle. */ /* * * Copyright (c) 2004 International Business Machines, Inc. * * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef __STONITH_PLUGIN_H # define __STONITH_PLUGIN_H #include #include typedef struct stonith_plugin StonithPlugin; #define NUM_STONITH_FNS 7 struct stonith_ops { StonithPlugin * (*new) (const char*); /* mini-Constructor */ void (*destroy) (StonithPlugin*); /*(full) Destructor */ const char* (*get_info) (StonithPlugin*, int infotype); const char * const * (*get_confignames) (StonithPlugin*); int (*set_config) (StonithPlugin*, StonithNVpair* list); /* Finishes construction */ /* * Must call set_config before calling any of * the member functions below... */ int (*get_status) (StonithPlugin*s); int (*req_reset) (StonithPlugin*, int op, const char* node); char** (*get_hostlist) (StonithPlugin*); /* Returns list of hosts it supports */ }; struct stonith_plugin { Stonith s; struct stonith_ops* s_ops; gboolean isconfigured; }; #define STONITH_TYPE stonith2 #define STONITH_TYPE_S "stonith2" typedef struct StonithImports_s StonithImports; struct Etoken { const char * string; /* The token to look for */ int toktype; /* The type to return on match */ int matchto; /* Modified during matches */ }; /* An array of StonithNamesToGet is terminated by a NULL s_name */ typedef struct { const char * s_name; char * s_value; }StonithNamesToGet; #define TELNET_PORT 23 #define TELNET_SERVICE "telnet" struct StonithImports_s { int (*ExpectToken)(int fd, struct Etoken * toklist, int to_secs , char * buf, int maxline, int debug); int (*StartProcess)(const char * cmd, int * readfd, int * writefd); int (*OpenStreamSocket) (const char * host, int port , const char * service); /* Service can be NULL, port can be <= 0, but not both... */ const char* (*GetValue)(StonithNVpair*, const char * name); int (*CopyAllValues) (StonithNamesToGet* out, StonithNVpair* in); char **(*StringToHostList)(const char * hlstring); char **(*CopyHostList)(const char * const * hlstring); void (*FreeHostList)(char** hostlist); int (*TtyLock)(const char* tty); int (*TtyUnlock)(const char* tty); }; /* * A few standardized parameter names */ #define ST_HOSTLIST "hostlist" #define ST_IPADDR "ipaddr" #define ST_LOGIN "login" #define ST_PASSWD "password" #define ST_COMMUNITY "community" /* SNMP community */ #define ST_TTYDEV "ttydev" /* TTY device name */ #endif /*__STONITH__PLUGIN_H*/ Reusable-Cluster-Components-glue--3cff550e1084/lib/Makefile.am0000644000000000000000000000147512120057602024072 0ustar00usergroup00000000000000# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = pils clplumbing lrm stonith plugins Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/GSource.c0000644000000000000000000012362712120057602025711 0ustar00usergroup00000000000000/* * Copyright (c) 2002 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef events # undef events #endif #ifdef revents # undef revents #endif #define DEFAULT_MAXDISPATCH 0 #define DEFAULT_MAXDELAY 0 #define OTHER_MAXDELAY 100 /* * On architectures with alignment constraints, our casting between * "(GSource*)" and "(GFDSource_s*)" etc. causes trouble, because of * the massive alignment requirements of "longclock_t". * * Use the following to store and fetch. */ static void lc_store(char *destptr, longclock_t value) { longclock_t _ltt; _ltt = value; memcpy((destptr), &_ltt, sizeof(longclock_t)); } static longclock_t lc_fetch(char *ptr) { longclock_t _ltt; memcpy(&_ltt, (ptr), sizeof(longclock_t)); return _ltt; } #define ERR_EVENTS (G_IO_ERR|G_IO_NVAL) #define INPUT_EVENTS (G_IO_IN|G_IO_PRI|G_IO_HUP) #define OUTPUT_EVENTS (G_IO_OUT) #define DEF_EVENTS (INPUT_EVENTS|ERR_EVENTS) #define WARN_DELAY(ms, mx, input) cl_log(LOG_WARNING \ , "%s: Dispatch function for %s was delayed" \ " %lu ms (> %lu ms) before being called (GSource: 0x%lx)" \ , __FUNCTION__, (input)->description, ms, mx \ , POINTER_TO_ULONG(input)) #define EXPLAINDELAY(started, detected) cl_log(LOG_INFO \ , "%s: started at %llu should have started at %llu" \ , __FUNCTION__, (unsigned long long)started \ , (unsigned long long)detected) #define WARN_TOOLONG(ms, mx, input) cl_log(LOG_WARNING \ , "%s: Dispatch function for %s took too long to execute" \ ": %lu ms (> %lu ms) (GSource: 0x%lx)" \ , __FUNCTION__, (input)->description, ms, mx \ , POINTER_TO_ULONG(input)) #define CHECK_DISPATCH_DELAY(i) { \ unsigned long ms; \ longclock_t dettime; \ dispstart = time_longclock(); \ dettime = lc_fetch((i)->detecttime); \ ms = longclockto_ms(sub_longclock(dispstart,dettime)); \ if ((i)->maxdispatchdelayms > 0 \ && ms > (i)->maxdispatchdelayms) { \ WARN_DELAY(ms, (i)->maxdispatchdelayms, (i)); \ EXPLAINDELAY(dispstart, dettime); \ } \ } #define CHECK_DISPATCH_TIME(i) { \ unsigned long ms; \ longclock_t dispend = time_longclock(); \ ms = longclockto_ms(sub_longclock(dispend, dispstart)); \ if ((i)->maxdispatchms > 0 && ms > (i)->maxdispatchms) { \ WARN_TOOLONG(ms, (i)->maxdispatchms, (i)); \ } \ lc_store(((i)->detecttime), zero_longclock); \ } #define WARN_TOOMUCH(ms, mx, input) cl_log(LOG_WARNING \ , "%s: working on %s took %ld ms (> %ld ms)" \ , __FUNCTION__, (input)->description, ms, mx); #define SAVESTART {funstart = time_longclock();} #define CHECKEND(input) { \ longclock_t funend = time_longclock(); \ long ms; \ ms = longclockto_ms(sub_longclock(funend, funstart)); \ if (ms > OTHER_MAXDELAY){ \ WARN_TOOMUCH(ms, ((long) OTHER_MAXDELAY), input); \ } \ } \ #ifndef _NSIG # define _NSIG 2*NSIG #endif static gboolean G_fd_prepare(GSource* source, gint* timeout); static gboolean G_fd_check(GSource* source); static gboolean G_fd_dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void G_fd_destroy(GSource* source); static GSourceFuncs G_fd_SourceFuncs = { G_fd_prepare, G_fd_check, G_fd_dispatch, G_fd_destroy, }; GSource* G_main_add_input(int priority, gboolean can_recurse, GSourceFuncs* funcs) { GSource * input_source = g_source_new(funcs, sizeof(GSource)); if (input_source == NULL){ cl_log(LOG_ERR, "create glib source for input failed!"); }else { g_source_set_priority(input_source, priority); g_source_set_can_recurse(input_source, can_recurse); if(g_source_attach(input_source, NULL) == 0){ cl_log(LOG_ERR, "attaching input_source to main context" " failed!! "); } } return input_source; } /* * Add the given file descriptor to the gmainloop world. */ GFDSource* G_main_add_fd(int priority, int fd, gboolean can_recurse , gboolean (*dispatch)(int fd, gpointer user_data) , gpointer userdata , GDestroyNotify notify) { GSource* source = g_source_new(&G_fd_SourceFuncs, sizeof(GFDSource)); GFDSource* ret = (GFDSource*)source; ret->magno = MAG_GFDSOURCE; ret->maxdispatchdelayms = DEFAULT_MAXDELAY; ret->maxdispatchms = DEFAULT_MAXDISPATCH; ret->udata = userdata; ret->dispatch = dispatch; ret->gpfd.fd = fd; ret->gpfd.events = DEF_EVENTS; ret->gpfd.revents = 0; ret->dnotify = notify; lc_store((ret->detecttime), zero_longclock); g_source_add_poll(source, &ret->gpfd); g_source_set_priority(source, priority); g_source_set_can_recurse(source, can_recurse); ret->gsourceid = g_source_attach(source, NULL); ret->description = "file descriptor"; if (ret->gsourceid == 0) { g_source_remove_poll(source, &ret->gpfd); memset(ret, 0, sizeof(GFDSource)); g_source_unref(source); source = NULL; ret = NULL; } return ret; } gboolean G_main_del_fd(GFDSource* fdp) { GSource * source = (GSource*) fdp; if (fdp->gsourceid <= 0) { return FALSE; } g_source_remove_poll(source, &fdp->gpfd); g_source_remove(fdp->gsourceid); fdp->gsourceid = 0; g_source_unref(source); return TRUE; } void g_main_output_is_blocked(GFDSource* fdp) { fdp->gpfd.events |= OUTPUT_EVENTS; } /* * For pure file descriptor events, return FALSE because we * have to poll to get events. * * Note that we don't modify 'timeout' either. */ static gboolean G_fd_prepare(GSource* source, gint* timeout) { GFDSource* fdp = (GFDSource*)source; g_assert(IS_FDSOURCE(fdp)); return FALSE; } /* * Did we notice any I/O events? */ static gboolean G_fd_check(GSource* source) { GFDSource* fdp = (GFDSource*)source; g_assert(IS_FDSOURCE(fdp)); if (fdp->gpfd.revents) { lc_store((fdp->detecttime), time_longclock()); return TRUE; } return FALSE; } /* * Some kind of event occurred - notify the user. */ static gboolean G_fd_dispatch(GSource* source, GSourceFunc callback, gpointer user_data) { GFDSource* fdp = (GFDSource*)source; longclock_t dispstart; g_assert(IS_FDSOURCE(fdp)); CHECK_DISPATCH_DELAY(fdp); /* * Is output now unblocked? * * If so, turn off OUTPUT_EVENTS to avoid going into * a tight poll(2) loop. */ if (fdp->gpfd.revents & OUTPUT_EVENTS) { fdp->gpfd.events &= ~OUTPUT_EVENTS; } if(fdp->dispatch) { if(!(fdp->dispatch(fdp->gpfd.fd, fdp->udata))){ g_source_remove_poll(source,&fdp->gpfd); g_source_unref(source); CHECK_DISPATCH_TIME(fdp); return FALSE; } CHECK_DISPATCH_TIME(fdp); } return TRUE; } /* * Free up our data, and notify the user process... */ static void G_fd_destroy(GSource* source) { GFDSource* fdp = (GFDSource*)source; g_assert(IS_FDSOURCE(fdp)); fdp->gsourceid = 0; if (fdp->dnotify) { fdp->dnotify(fdp->udata); } } /************************************************************ * Functions for IPC_Channels ***********************************************************/ gboolean G_CH_prepare_int(GSource* source, gint* timeout); gboolean G_CH_check_int(GSource* source); gboolean G_CH_dispatch_int(GSource* source, GSourceFunc callback, gpointer user_data); void G_CH_destroy_int(GSource* source); static GSourceFuncs G_CH_SourceFuncs = { G_CH_prepare_int, G_CH_check_int, G_CH_dispatch_int, G_CH_destroy_int, }; void set_IPC_Channel_dnotify(GCHSource* chp, GDestroyNotify notify){ chp->dnotify = notify; } /* * Add an IPC_channel to the gmainloop world... */ GCHSource* G_main_IPC_Channel_constructor(GSource* source, IPC_Channel* ch , gpointer userdata , GDestroyNotify notify) { int rfd, wfd; GCHSource* chp; if( !source ) { cl_log(LOG_WARNING, "%s:%d: got null source", __FUNCTION__,__LINE__); return NULL; } if( !ch ) { cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__); return NULL; } chp = (GCHSource*)source; chp->magno = MAG_GCHSOURCE; chp->maxdispatchdelayms = DEFAULT_MAXDELAY; chp->maxdispatchms = DEFAULT_MAXDISPATCH; lc_store((chp->detecttime), zero_longclock); ch->refcount++; chp->ch = ch; chp->udata=userdata; chp->dnotify = notify; chp->dontread = FALSE; rfd = ch->ops->get_recv_select_fd(ch); wfd = ch->ops->get_send_select_fd(ch); chp->fd_fdx = (rfd == wfd); if (debug_level > 1) { cl_log(LOG_DEBUG, "%s(sock=%d,%d)",__FUNCTION__, rfd,wfd); } chp->infd.fd = rfd; chp->infd.events = DEF_EVENTS; g_source_add_poll(source, &chp->infd); if (!chp->fd_fdx) { chp->outfd.fd = wfd; chp->outfd.events = DEF_EVENTS; g_source_add_poll(source, &chp->outfd); } chp->dispatch = NULL; chp->description = "IPC channel(base)"; chp->gsourceid = 0; return chp; } GCHSource* G_main_add_IPC_Channel(int priority, IPC_Channel* ch , gboolean can_recurse , gboolean (*dispatch)(IPC_Channel* source_data, gpointer user_data) , gpointer userdata , GDestroyNotify notify) { GCHSource *chp; GSource *source; if( !ch ) { cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__); return NULL; } source = g_source_new(&G_CH_SourceFuncs, sizeof(GCHSource)); G_main_IPC_Channel_constructor(source,ch,userdata,notify); chp = (GCHSource*)source; chp->dispatch = dispatch; g_source_set_priority(source, priority); g_source_set_can_recurse(source, can_recurse); chp->gsourceid = g_source_attach(source, NULL); chp->description = "IPC channel"; if (chp->gsourceid == 0) { g_source_remove_poll(source, &chp->infd); if (!chp->fd_fdx) { g_source_remove_poll(source, &chp->outfd); } g_source_unref(source); source = NULL; chp = NULL; } return chp; } void /* Suspend reading from far end writer (flow control) */ G_main_IPC_Channel_pause(GCHSource* chp) { if (chp == NULL){ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__); return; } chp->dontread = TRUE; return; } void /* Resume reading from far end writer (un-flow-control) */ G_main_IPC_Channel_resume(GCHSource* chp) { if (chp == NULL){ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__); return; } chp->dontread = FALSE; return; } /* * Delete an IPC_channel from the gmainloop world... */ gboolean G_main_del_IPC_Channel(GCHSource* chp) { GSource* source = (GSource*) chp; if (chp == NULL || chp->gsourceid <= 0) { return FALSE; } if (debug_level > 1) { cl_log(LOG_DEBUG, "%s(sock=%d)",__FUNCTION__, chp->infd.fd); } g_source_remove(chp->gsourceid); chp->gsourceid = 0; /* chp should (may) now be undefined */ g_source_unref(source); return TRUE; } /* * For IPC_CHANNEL events, enable output checking when needed * and note when unread input is already queued. * * Note that we don't modify 'timeout' either. */ gboolean G_CH_prepare_int(GSource* source, gint* timeout) { GCHSource* chp = (GCHSource*)source; longclock_t funstart; gboolean ret; g_assert(IS_CHSOURCE(chp)); SAVESTART; if (chp->ch->ops->is_sending_blocked(chp->ch)) { if (chp->fd_fdx) { chp->infd.events |= OUTPUT_EVENTS; }else{ chp->outfd.events |= OUTPUT_EVENTS; } } if (chp->ch->recv_queue->current_qlen < chp->ch->recv_queue->max_qlen) { chp->infd.events |= INPUT_EVENTS; }else{ /* * This also disables EOF events - until we * read some of the packets we've already gotten * This prevents a tight loop in poll(2). */ chp->infd.events &= ~INPUT_EVENTS; } if (chp->dontread){ return FALSE; } ret = chp->ch->ops->is_message_pending(chp->ch); if (ret) { lc_store((chp->detecttime), time_longclock()); } CHECKEND(chp); return ret; } /* * Did we notice any I/O events? */ gboolean G_CH_check_int(GSource* source) { GCHSource* chp = (GCHSource*)source; gboolean ret; longclock_t funstart; g_assert(IS_CHSOURCE(chp)); SAVESTART; if (chp->dontread){ /* Make sure output gets unblocked */ chp->ch->ops->resume_io(chp->ch); return FALSE; } ret = (chp->infd.revents != 0 || (!chp->fd_fdx && chp->outfd.revents != 0) || chp->ch->ops->is_message_pending(chp->ch)); if (ret) { lc_store((chp->detecttime), time_longclock()); } CHECKEND(chp); return ret; } /* * Some kind of event occurred - notify the user. */ gboolean G_CH_dispatch_int(GSource * source, GSourceFunc callback, gpointer user_data) { GCHSource* chp = (GCHSource*)source; longclock_t dispstart; longclock_t resume_start = zero_longclock; g_assert(IS_CHSOURCE(chp)); CHECK_DISPATCH_DELAY(chp); if (chp->dontread){ return TRUE; } /* Is output now unblocked? * * If so, turn off OUTPUT_EVENTS to avoid going into * a tight poll(2) loop. */ if (chp->fd_fdx) { if (chp->infd.revents & OUTPUT_EVENTS) { chp->infd.events &= ~OUTPUT_EVENTS; } }else if (chp->outfd.revents & OUTPUT_EVENTS) { chp->outfd.events &= ~OUTPUT_EVENTS; } if (ANYDEBUG) { resume_start = time_longclock(); } chp->ch->ops->resume_io(chp->ch); if (ANYDEBUG) { longclock_t resume_end = time_longclock(); unsigned long ms; ms = longclockto_ms(sub_longclock(resume_end , resume_start)); if (ms > 10) { cl_log(LOG_WARNING , "%s: resume_io() for %s took %lu ms" , __FUNCTION__ , chp->description, ms); } } if(chp->dispatch && chp->ch->ops->is_message_pending(chp->ch)) { if(!(chp->dispatch(chp->ch, chp->udata))){ g_source_remove_poll(source, &chp->infd); if (!chp->fd_fdx) { g_source_remove_poll(source, &chp->outfd); } CHECK_DISPATCH_TIME(chp); g_source_unref(source); return FALSE; } } CHECK_DISPATCH_TIME(chp); if (chp->ch->ch_status == IPC_DISCONNECT){ return FALSE; } return TRUE; } /* * Free up our data, and notify the user process... */ void G_CH_destroy_int(GSource* source) { GCHSource* chp = (GCHSource*)source; g_assert(IS_CHSOURCE(chp)); if (debug_level > 1) { cl_log(LOG_DEBUG, "%s(chp=0x%lx, sock=%d) {", __FUNCTION__ , (unsigned long)chp, chp->infd.fd); } if (chp->dnotify) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: Calling dnotify(sock=%d, arg=0x%lx) function" , __FUNCTION__, chp->infd.fd, (unsigned long)chp->udata); } chp->dnotify(chp->udata); }else{ if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: NOT calling dnotify(sock=%d) function" , __FUNCTION__, chp->infd.fd); } } if (chp->ch) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: calling IPC destroy (chp->ch=0x%lx, sock=%d)" , __FUNCTION__ , (unsigned long)chp->ch, chp->infd.fd); } chp->ch->ops->destroy(chp->ch); chp->ch = NULL; } /*chp->gsourceid = 0; ?*/ if (debug_level > 1) { cl_log(LOG_DEBUG, "}/*%s(sock=%d)*/", __FUNCTION__, chp->infd.fd); } } /************************************************************ * Functions for IPC_WaitConnections ***********************************************************/ static gboolean G_WC_prepare(GSource * source, gint* timeout); static gboolean G_WC_check(GSource* source); static gboolean G_WC_dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void G_WC_destroy(GSource* source); static GSourceFuncs G_WC_SourceFuncs = { G_WC_prepare, G_WC_check, G_WC_dispatch, G_WC_destroy, }; /* * Add an IPC_WaitConnection to the gmainloop world... */ GWCSource* G_main_add_IPC_WaitConnection(int priority , IPC_WaitConnection* wch , IPC_Auth* auth_info , gboolean can_recurse , gboolean (*dispatch)(IPC_Channel* wch , gpointer user_data) , gpointer userdata , GDestroyNotify notify) { GWCSource* wcp; GSource * source = g_source_new(&G_WC_SourceFuncs, sizeof(GWCSource)); wcp = (GWCSource*)source; wcp->magno = MAG_GWCSOURCE; wcp->maxdispatchdelayms = DEFAULT_MAXDELAY; wcp->maxdispatchms = DEFAULT_MAXDISPATCH; lc_store((wcp->detecttime), zero_longclock); wcp->udata = userdata; wcp->gpfd.fd = wch->ops->get_select_fd(wch); wcp->gpfd.events = DEF_EVENTS; wcp->gpfd.revents = 0; wcp->wch = wch; wcp->dnotify = notify; wcp->auth_info = auth_info; wcp->dispatch = dispatch; g_source_add_poll(source, &wcp->gpfd); g_source_set_priority(source, priority); g_source_set_can_recurse(source, can_recurse); wcp->gsourceid = g_source_attach(source, NULL); wcp->description = "IPC wait for connection"; if (wcp->gsourceid == 0) { g_source_remove_poll(source, &wcp->gpfd); g_source_unref(source); source = NULL; wcp = NULL; } return wcp; } /* Delete the given IPC_WaitConnection from the gmainloop world */ gboolean G_main_del_IPC_WaitConnection(GWCSource* wcp) { GSource* source = (GSource*) wcp; if (wcp->gsourceid <= 0) { return FALSE; } g_source_remove(wcp->gsourceid); wcp->gsourceid = 0; g_source_unref(source); return TRUE; } /* * For IPC_WaitConnection events, return FALSE because we * have to poll to get events. * * We don't modify 'timeout' either. */ static gboolean G_WC_prepare(GSource* source, gint* timeout) { GWCSource* wcp = (GWCSource*)source; g_assert(IS_WCSOURCE(wcp)); return FALSE; } /* * Did we notice any I/O (connection pending) events? */ static gboolean G_WC_check(GSource * source) { GWCSource* wcp = (GWCSource*)source; g_assert(IS_WCSOURCE(wcp)); if (wcp->gpfd.revents != 0) { lc_store((wcp->detecttime), time_longclock()); return TRUE; } return FALSE; } /* * Someone is trying to connect. * Try to accept the connection and notify the user. */ static gboolean G_WC_dispatch(GSource* source, GSourceFunc callback, gpointer user_data) { GWCSource* wcp = (GWCSource*)source; IPC_Channel* ch; gboolean rc = TRUE; int count = 0; longclock_t dispstart; g_assert(IS_WCSOURCE(wcp)); CHECK_DISPATCH_DELAY(wcp); while(1) { ch = wcp->wch->ops->accept_connection(wcp->wch, wcp->auth_info); if (ch == NULL) { if (errno == EBADF) { cl_perror("%s: Stopping accepting connections(socket=%d)!!" , __FUNCTION__, wcp->gpfd.fd); rc = FALSE; } break; } ++count; if(!wcp->dispatch) { continue; } rc = wcp->dispatch(ch, wcp->udata); if(!rc) { g_source_remove_poll(source, &wcp->gpfd); g_source_unref(source); break; } } CHECK_DISPATCH_TIME(wcp); return rc; } /* * Free up our data, and notify the user process... */ static void G_WC_destroy(GSource* source) { GWCSource* wcp = (GWCSource*)source; wcp->gsourceid = 0; g_assert(IS_WCSOURCE(wcp)); wcp->wch->ops->destroy(wcp->wch); if (wcp->dnotify) { wcp->dnotify(wcp->udata); } } /************************************************************ * Functions for Signals ***********************************************************/ static gboolean G_SIG_prepare(GSource* source, gint* timeout); static gboolean G_SIG_check(GSource* source); static gboolean G_SIG_dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void G_SIG_destroy(GSource* source); static void G_main_signal_handler(int nsig); static GSourceFuncs G_SIG_SourceFuncs = { G_SIG_prepare, G_SIG_check, G_SIG_dispatch, G_SIG_destroy, }; static GSIGSource *G_main_signal_list[_NSIG]; void set_SignalHandler_dnotify(GSIGSource* sig_src, GDestroyNotify notify) { sig_src->dnotify = notify; } /* * Add an Signal to the gmainloop world... */ GSIGSource* G_main_add_SignalHandler(int priority, int signal, gboolean (*dispatch)(int nsig, gpointer user_data), gpointer userdata, GDestroyNotify notify) { GSIGSource* sig_src; GSource * source = g_source_new(&G_SIG_SourceFuncs, sizeof(GSIGSource)); gboolean failed = FALSE; sig_src = (GSIGSource*)source; sig_src->magno = MAG_GSIGSOURCE; sig_src->maxdispatchdelayms = DEFAULT_MAXDELAY; sig_src->maxdispatchms = DEFAULT_MAXDISPATCH; sig_src->signal = signal; sig_src->dispatch = dispatch; sig_src->udata = userdata; sig_src->dnotify = notify; sig_src->signal_triggered = FALSE; g_source_set_priority(source, priority); g_source_set_can_recurse(source, FALSE); if(G_main_signal_list[signal] != NULL) { cl_log(LOG_ERR , "%s: Handler already present for signal %d" , __FUNCTION__, signal); failed = TRUE; } if(!failed) { sig_src->gsourceid = g_source_attach(source, NULL); sig_src->description = "signal"; if (sig_src->gsourceid < 1) { cl_log(LOG_ERR , "%s: Could not attach source for signal %d (%d)" , __FUNCTION__ , signal, sig_src->gsourceid); failed = TRUE; } } if(failed) { cl_log(LOG_ERR , "%s: Signal handler for signal %d NOT added" , __FUNCTION__, signal); g_source_remove(sig_src->gsourceid); g_source_unref(source); source = NULL; sig_src = NULL; } else { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: Added signal handler for signal %d" , __FUNCTION__, signal); } G_main_signal_list[signal] = sig_src; CL_SIGNAL(signal, G_main_signal_handler); /* * If we don't set this on, then the mainloop poll(2) call * will never be interrupted by this signal - which sort of * defeats the whole purpose of a signal handler in a * mainloop program */ cl_signal_set_interrupt(signal, TRUE); } return sig_src; } /* * Delete a Signal from the gmainloop world... */ gboolean G_main_del_SignalHandler(GSIGSource* sig_src) { GSource* source = (GSource*) sig_src; if (sig_src->gsourceid <= 0) { return FALSE; } if(_NSIG <= sig_src->signal) { g_assert(_NSIG > sig_src->signal); return FALSE; } CL_SIGNAL(sig_src->signal, NULL); sig_src->signal_triggered = FALSE; g_source_remove(sig_src->gsourceid); G_main_signal_list[sig_src->signal] = NULL; sig_src->gsourceid = 0; g_source_unref(source); return TRUE; } static gboolean G_SIG_prepare(GSource* source, gint* timeoutms) { GSIGSource* sig_src = (GSIGSource*)source; g_assert(IS_SIGSOURCE(sig_src)); /* Don't let a timing window keep us in poll() forever * * The timing window in question looks like this: * No signal has occurred up to the point of prepare being called. * Signal comes in _after_ prepare was called, but _before_ poll. * signal_detected gets set, but no one checks it before going into poll * We wait in poll forever... It's not a pretty sight :-(. */ *timeoutms = 1000; /* Sigh... */ if (sig_src->signal_triggered) { clock_t now; clock_t diff; /* detecttime is reset in the dispatch function */ if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0) { cl_log(LOG_ERR, "%s: detecttime already set?", __FUNCTION__); return TRUE; } /* Otherwise, this is when it was first detected */ now = cl_times(); diff = now - sig_src->sh_detecttime; /* How long since signal occurred? */ lc_store( sig_src->detecttime, sub_longclock(time_longclock(), (longclock_t)diff) ); return TRUE; } return FALSE; } /* * Did we notice any I/O events? */ static gboolean G_SIG_check(GSource* source) { GSIGSource* sig_src = (GSIGSource*)source; g_assert(IS_SIGSOURCE(sig_src)); if (sig_src->signal_triggered) { clock_t now; clock_t diff; if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0){ return TRUE; } /* Otherwise, this is when it was first detected */ now = cl_times(); diff = now - sig_src->sh_detecttime; lc_store( sig_src->detecttime, sub_longclock(time_longclock(), (longclock_t)diff) ); return TRUE; } return FALSE; } /* * Some kind of event occurred - notify the user. */ static gboolean G_SIG_dispatch(GSource * source, GSourceFunc callback, gpointer user_data) { GSIGSource* sig_src = (GSIGSource*)source; longclock_t dispstart; g_assert(IS_SIGSOURCE(sig_src)); CHECK_DISPATCH_DELAY(sig_src); sig_src->sh_detecttime = 0UL; sig_src->signal_triggered = FALSE; if(sig_src->dispatch) { if(!(sig_src->dispatch(sig_src->signal, sig_src->udata))){ G_main_del_SignalHandler(sig_src); CHECK_DISPATCH_TIME(sig_src); return FALSE; } } CHECK_DISPATCH_TIME(sig_src); return TRUE; } /* * Free up our data, and notify the user process... */ static void G_SIG_destroy(GSource* source) { GSIGSource* sig_src = (GSIGSource*)source; g_assert(IS_SIGSOURCE(sig_src)); sig_src->gsourceid = 0; if (sig_src->dnotify) { sig_src->dnotify(sig_src->udata); } } /* Find and set the correct mainloop input */ static void G_main_signal_handler(int nsig) { GSIGSource* sig_src = NULL; if(G_main_signal_list == NULL) { g_assert(G_main_signal_list != NULL); return; } if(_NSIG <= nsig) { g_assert(_NSIG > nsig); return; } sig_src = G_main_signal_list[nsig]; if(sig_src == NULL) { /* cl_log(LOG_CRIT, "No handler for signal -%d", nsig); */ return; } g_assert(IS_SIGSOURCE(sig_src)); /* Time from first occurance of signal */ if (!sig_src->signal_triggered) { /* Avoid calling longclock_time() on a signal */ sig_src->sh_detecttime=cl_times(); } sig_src->signal_triggered = TRUE; } /* * Functions to handle child process */ #define WAITALARM 5000L /* milliseconds */ static int alarm_count = 0; static void G_main_alarm_helper(int nsig) { ++alarm_count; } static gboolean child_death_dispatch(int sig, gpointer notused) { int status; pid_t pid; const int waitflags = WNOHANG; struct sigaction saveaction; int childcount = 0; /* * wait3(WNOHANG) isn't _supposed_ to hang * Unfortunately, it seems to do just that on some OSes. * * The workaround is to set an alarm. I don't think for this purpose * that it matters if siginterrupt(SIGALRM) is set TRUE or FALSE since * the tiniest little excuse seems to cause the wait3() to finish. */ memset(&saveaction, 0, sizeof(saveaction)); cl_signal_set_simple_handler(SIGALRM, G_main_alarm_helper, &saveaction); alarm_count = 0; cl_signal_set_interrupt(SIGALRM, TRUE); setmsrepeattimer(WAITALARM); /* Might as well be persistent ;-) */ while((pid=wait3(&status, waitflags, NULL)) > 0 || (pid < 0 && errno == EINTR)) { cancelmstimer(); if (pid > 0) { ++childcount; ReportProcHasDied(pid, status); } setmsrepeattimer(WAITALARM); /* Let's be persistent ;-) */ } cancelmstimer(); cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction); if (pid < 0 && errno != ECHILD) { cl_perror("%s: wait3() failed" , __FUNCTION__); } #if defined(DEBUG) if (childcount < 1) { /* * This happens when we receive a SIGCHLD after we clear * 'sig_src->signal_triggered' in G_SIG_dispatch() but * before the last wait3() call returns no child above. */ cl_log(LOG_DEBUG, "NOTE: %s called without children to wait on" , __FUNCTION__); } #endif if (alarm_count) { cl_log(LOG_ERR , "%s: wait3() call hung %d times. childcount = %d" , __FUNCTION__, alarm_count, childcount); alarm_count = 0; } return TRUE; } void set_sigchld_proctrack(int priority, unsigned long maxdisptime) { GSIGSource* src = G_main_add_SignalHandler(priority, SIGCHLD , child_death_dispatch, NULL, NULL); G_main_setmaxdispatchdelay((GSource*) src, 100); G_main_setmaxdispatchtime((GSource*) src, maxdisptime); G_main_setdescription((GSource*)src, "SIGCHLD"); return; } /************************************************************ * Functions for Trigger inputs ***********************************************************/ static gboolean G_TRIG_prepare(GSource* source, gint* timeout); static gboolean G_TRIG_check(GSource* source); static gboolean G_TRIG_dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void G_TRIG_destroy(GSource* source); static GSourceFuncs G_TRIG_SourceFuncs = { G_TRIG_prepare, G_TRIG_check, G_TRIG_dispatch, G_TRIG_destroy }; void set_TriggerHandler_dnotify(GTRIGSource* trig_src, GDestroyNotify notify) { trig_src->dnotify = notify; } /* * Add an Trigger to the gmainloop world... */ GTRIGSource* G_main_add_TriggerHandler(int priority, gboolean (*dispatch)(gpointer user_data), gpointer userdata, GDestroyNotify notify) { GTRIGSource* trig_src = NULL; GSource * source = g_source_new(&G_TRIG_SourceFuncs, sizeof(GTRIGSource)); gboolean failed = FALSE; trig_src = (GTRIGSource*)source; trig_src->magno = MAG_GTRIGSOURCE; trig_src->maxdispatchdelayms = DEFAULT_MAXDELAY; trig_src->maxdispatchms = DEFAULT_MAXDISPATCH; trig_src->dispatch = dispatch; trig_src->udata = userdata; trig_src->dnotify = notify; lc_store((trig_src->detecttime), zero_longclock); trig_src->manual_trigger = FALSE; g_source_set_priority(source, priority); g_source_set_can_recurse(source, FALSE); if(!failed) { trig_src->gsourceid = g_source_attach(source, NULL); trig_src->description = "trigger"; if (trig_src->gsourceid < 1) { cl_log(LOG_ERR, "G_main_add_TriggerHandler: Could not attach new source (%d)", trig_src->gsourceid); failed = TRUE; } } if(failed) { cl_log(LOG_ERR, "G_main_add_TriggerHandler: Trigger handler NOT added"); g_source_remove(trig_src->gsourceid); g_source_unref(source); source = NULL; trig_src = NULL; } else { if (debug_level > 1) { cl_log(LOG_DEBUG, "G_main_add_TriggerHandler: Added signal manual handler"); } } return trig_src; } void G_main_set_trigger(GTRIGSource* source) { GTRIGSource* trig_src = (GTRIGSource*)source; g_assert(IS_TRIGSOURCE(trig_src)); trig_src->manual_trigger = TRUE; lc_store((trig_src->detecttime), time_longclock()); } /* * Delete a Trigger from the gmainloop world... */ gboolean G_main_del_TriggerHandler(GTRIGSource* trig_src) { GSource* source = (GSource*) trig_src; if (trig_src->gsourceid <= 0) { return FALSE; } trig_src->gsourceid = 0; trig_src->manual_trigger = FALSE; g_source_remove(trig_src->gsourceid); g_source_unref(source); return TRUE; } static gboolean G_TRIG_prepare(GSource* source, gint* timeout) { GTRIGSource* trig_src = (GTRIGSource*)source; g_assert(IS_TRIGSOURCE(trig_src)); if (trig_src->manual_trigger && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) { lc_store((trig_src->detecttime), time_longclock()); } return trig_src->manual_trigger; } /* * Did we notice any I/O events? */ static gboolean G_TRIG_check(GSource* source) { GTRIGSource* trig_src = (GTRIGSource*)source; g_assert(IS_TRIGSOURCE(trig_src)); if (trig_src->manual_trigger && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) { lc_store((trig_src->detecttime), time_longclock()); } return trig_src->manual_trigger; } /* * Some kind of event occurred - notify the user. */ static gboolean G_TRIG_dispatch(GSource * source, GSourceFunc callback, gpointer user_data) { GTRIGSource* trig_src = (GTRIGSource*)source; longclock_t dispstart; g_assert(IS_TRIGSOURCE(trig_src)); CHECK_DISPATCH_DELAY(trig_src); trig_src->manual_trigger = FALSE; if(trig_src->dispatch) { if(!(trig_src->dispatch(trig_src->udata))){ G_main_del_TriggerHandler(trig_src); CHECK_DISPATCH_TIME(trig_src); return FALSE; } CHECK_DISPATCH_TIME(trig_src); } lc_store((trig_src->detecttime), zero_longclock); return TRUE; } /* * Free up our data, and notify the user process... */ static void G_TRIG_destroy(GSource* source) { GTRIGSource* trig_src = (GTRIGSource*)source; g_assert(IS_TRIGSOURCE(trig_src)); trig_src->gsourceid = 0; if (trig_src->dnotify) { trig_src->dnotify(trig_src->udata); } } /* * Glib mainloop timeout handling code. * * These functions work correctly even if someone resets the * time-of-day clock. The g_main_timeout_add() function does not have * this property, since it relies on gettimeofday(). * * Our functions have the same semantics - except they always work ;-) * * This is because we use longclock_t for our time values. * */ static gboolean Gmain_timeout_prepare(GSource* src, gint* timeout); static gboolean Gmain_timeout_check(GSource* src); static gboolean Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data); static GSourceFuncs Gmain_timeout_funcs = { Gmain_timeout_prepare, Gmain_timeout_check, Gmain_timeout_dispatch, }; struct GTimeoutAppend { COMMON_STRUCTSTART; longclock_t nexttime; guint interval; }; #define GTIMEOUT(GS) ((struct GTimeoutAppend*)((void*)(GS))) guint Gmain_timeout_add(guint interval , GSourceFunc function , gpointer data) { return Gmain_timeout_add_full(G_PRIORITY_DEFAULT , interval, function, data, NULL); } guint Gmain_timeout_add_full(gint priority , guint interval , GSourceFunc function , gpointer data , GDestroyNotify notify) { struct GTimeoutAppend* append; GSource* source = g_source_new( &Gmain_timeout_funcs, sizeof(struct GTimeoutAppend)); append = GTIMEOUT(source); append->magno = MAG_GTIMEOUTSRC; append->maxdispatchms = DEFAULT_MAXDISPATCH; append->maxdispatchdelayms = DEFAULT_MAXDELAY; append->description = "(timeout)"; lc_store((append->detecttime), zero_longclock); append->udata = NULL; append->nexttime = add_longclock(time_longclock() , msto_longclock(interval)); append->interval = interval; g_source_set_priority(source, priority); g_source_set_can_recurse(source, FALSE); g_source_set_callback(source, function, data, notify); append->gsourceid = g_source_attach(source, NULL); g_source_unref(source); return append->gsourceid; } void Gmain_timeout_remove(guint tag) { GSource* source = g_main_context_find_source_by_id(NULL,tag); struct GTimeoutAppend* append = GTIMEOUT(source); if (source == NULL){ cl_log(LOG_ERR, "Attempt to remove timeout (%u)" " with NULL source", tag); }else{ g_assert(IS_TIMEOUTSRC(append)); g_source_remove(tag); } return; } /* g_main_loop-style prepare function */ static gboolean Gmain_timeout_prepare(GSource* src, gint* timeout) { struct GTimeoutAppend* append = GTIMEOUT(src); longclock_t lnow = time_longclock(); longclock_t remain; g_assert(IS_TIMEOUTSRC(append)); if (cmp_longclock(lnow, append->nexttime) >= 0) { *timeout = 0L; return TRUE; } /* This is safe - we will always have a positive result */ remain = sub_longclock(append->nexttime, lnow); /* This is also safe - we started out in 'ms' */ *timeout = longclockto_ms(remain); return ((*timeout) == 0); } /* g_main_loop-style check function */ static gboolean Gmain_timeout_check (GSource* src) { struct GTimeoutAppend* append = GTIMEOUT(src); longclock_t lnow = time_longclock(); g_assert(IS_TIMEOUTSRC(append)); if (cmp_longclock(lnow, append->nexttime) >= 0) { return TRUE; } return FALSE; } /* g_main_loop-style dispatch function */ static gboolean Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data) { struct GTimeoutAppend* append = GTIMEOUT(src); longclock_t dispstart; gboolean ret; g_assert(IS_TIMEOUTSRC(append)); lc_store(append->detecttime, append->nexttime); CHECK_DISPATCH_DELAY(append); /* Schedule our next dispatch */ append->nexttime = add_longclock(time_longclock() , msto_longclock(append->interval)); /* Then call the user function */ ret = func(user_data); CHECK_DISPATCH_TIME(append); return ret; } void G_main_setmaxdispatchdelay(GSource* s, unsigned long delayms) { GFDSource* fdp = (GFDSource*)s; if (!IS_ONEOFOURS(fdp)) { cl_log(LOG_ERR , "Attempt to set max dispatch delay on wrong object"); return; } fdp->maxdispatchdelayms = delayms; } void G_main_setmaxdispatchtime(GSource* s, unsigned long dispatchms) { GFDSource* fdp = (GFDSource*)s; if (!IS_ONEOFOURS(fdp)) { cl_log(LOG_ERR , "Attempt to set max dispatch time on wrong object"); return; } fdp->maxdispatchms = dispatchms; } void G_main_setdescription(GSource* s, const char * description) { GFDSource* fdp = (GFDSource*)s; if (!IS_ONEOFOURS(fdp)) { cl_log(LOG_ERR , "Attempt to set max dispatch time on wrong object"); return; } fdp->description = description; } void G_main_setmaxdispatchdelay_id(guint id, unsigned long delayms) { GSource* source = g_main_context_find_source_by_id(NULL,id); if (source) { G_main_setmaxdispatchdelay(source, delayms); } } void G_main_setmaxdispatchtime_id(guint id, unsigned long dispatchms) { GSource* source = g_main_context_find_source_by_id(NULL,id); if (source) { G_main_setmaxdispatchtime(source, dispatchms); } } void G_main_setdescription_id(guint id, const char * description) { GSource* source = g_main_context_find_source_by_id(NULL,id); if (source) { G_main_setdescription(source, description); } } void G_main_setall_id(guint id, const char * description, unsigned long delay , unsigned long elapsed) { G_main_setdescription_id(id, description); G_main_setmaxdispatchdelay_id(id, delay); G_main_setmaxdispatchtime_id(id, elapsed); } static void TempProcessRegistered(ProcTrack* p); static void TempProcessDied(ProcTrack* p, int status, int signo , int exitcode, int waslogged); static const char* TempProcessName(ProcTrack* p); /*********************************************************************** * Track our temporary child processes... * * We run no more than one of each type at once. * If we need to run some and one is still running we run another one * when this one exits. * * Requests to run a child process don't add up. So, 3 requests to run * a child while one is running only cause it to be run once more, not * three times. * * The only guarantee is that a new child process will run after a request * was made. * * To create the possibility of running a particular type of child process * call G_main_add_tempproc_trigger(). * * To cause it to be run, call G_main_set_trigger(). * ***********************************************************************/ static ProcTrack_ops TempProcessTrackOps = { TempProcessDied, TempProcessRegistered, TempProcessName }; /* * Information for tracking our generic temporary child processes. */ struct tempproc_track { const char * procname; /* name of the process*/ GTRIGSource* trigger; /* Trigger for this event */ int (*fun)(gpointer userdata); /* Function to call * in child process */ void (*prefork)(gpointer userdata);/* Call before fork */ void (*postfork)(gpointer userdata);/* Call after fork */ void (*complete)(gpointer userdata, int status, int signo, int exitcode);/* Call after complete */ gpointer userdata; /* Info to pass 'fun' */ gboolean isrunning; /* TRUE if child is running */ gboolean runagain; /* TRUE if we need to run * again after child process * finishes. */ }; static void TempProcessRegistered(ProcTrack* p) { return; /* Don't need to do much here... */ } static void TempProcessDied(ProcTrack* p, int status, int signo, int exitcode , int waslogged) { struct tempproc_track * pt = p->privatedata; if (pt->complete) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: Calling 'complete' for temp process %s" , __FUNCTION__, pt->procname); } pt->complete(pt->userdata, status, signo, exitcode); } pt->isrunning=FALSE; if (pt->runagain) { pt->runagain=FALSE; /* Do it again, Sam! */ G_main_set_trigger(pt->trigger); /* Note that we set the trigger for this, we don't * fork or call the function now. * * This allows the mainloop scheduler to decide * when the fork should happen according to the priority * of this trigger event - NOT according to the priority * of general SIGCHLD handling. */ } p->privatedata = NULL; /* Don't free until trigger is destroyed */ return; } static const char * TempProcessName(ProcTrack* p) { struct tempproc_track * pt = p->privatedata; return pt->procname; } /* * Make sure only one copy is running at a time... */ static gboolean TempProcessTrigger(gpointer ginfo) { struct tempproc_track* info = ginfo; int pid; /* Make sure only one copy is running at a time. */ /* This avoids concurrency problems. */ if (info->isrunning) { info->runagain = TRUE; return TRUE; } info->isrunning = TRUE; if (info->prefork) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: Calling prefork for temp process %s" , __FUNCTION__, info->procname); } info->prefork(info->userdata); } if (ANYDEBUG) { cl_log(LOG_DEBUG, "Forking temp process %s", info->procname); } switch ((pid=fork())) { int rc; case -1: cl_perror("%s: Can't fork temporary child" " process [%s]!", __FUNCTION__ , info->procname); info->isrunning = FALSE; break; case 0: /* Child */ if ((rc=info->fun(info->userdata)) == HA_OK) { exit(0); } cl_log(LOG_WARNING , "%s: %s returns %d", __FUNCTION__ , info->procname, rc); exit(1); break; default: /* Fall out */; } if (pid > 0) { NewTrackedProc(pid, 0, (ANYDEBUG? PT_LOGVERBOSE : PT_LOGNORMAL) , ginfo, &TempProcessTrackOps); if (info->postfork) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: Calling postfork for temp process %s" , __FUNCTION__, info->procname); } info->postfork(info->userdata); } } return TRUE; } static void tempproc_destroy_notify(gpointer userdata) { if (userdata != NULL) { free(userdata); userdata = NULL; } } GTRIGSource* G_main_add_tempproc_trigger(int priority , int (*triggerfun) (gpointer p) , const char * procname , gpointer userdata , void (*prefork)(gpointer p) , void (*postfork)(gpointer p) , void (*complete)(gpointer userdata, int status, int signo, int exitcode)) { struct tempproc_track* p; GTRIGSource* ret; p = (struct tempproc_track *) malloc(sizeof(struct tempproc_track)); if (p == NULL) { return NULL; } memset(p, 0, sizeof(*p)); p->procname = procname; p->fun = triggerfun; p->userdata = userdata; p->prefork = prefork; p->postfork = postfork; p->complete = complete; ret = G_main_add_TriggerHandler(priority , TempProcessTrigger, p, tempproc_destroy_notify); if (ret == NULL) { free(p); p = NULL; }else{ p->trigger = ret; } return ret; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/Makefile.am0000644000000000000000000000614112120057602026221 0ustar00usergroup00000000000000# # plumbing: OCF general plumbing libraries # # Copyright (C) 2002 Alan Robertson, International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in halibdir = $(libdir)/@HB_PKG@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl ## libraries lib_LTLIBRARIES = libplumb.la libplumbgpl.la libplumb_la_SOURCES = \ base64.c \ cl_compress.c \ cl_log.c \ cl_misc.c \ cl_msg.c \ cl_msg_types.c \ cl_netstring.c \ cl_pidfile.c \ cl_poll.c \ cl_random.c \ cl_signal.c \ cl_syslog.c \ cl_uuid.c \ cl_plugin.c \ cl_reboot.c \ coredumps.c \ cpulimits.c \ GSource.c \ ipcsocket.c \ longclock.c \ md5.c \ mkstemp_mode.c \ ocf_ipc.c \ proctrack.c \ realtime.c \ replytrack.c \ timers.c \ uids.c libplumb_la_LIBADD = $(top_builddir)/replace/libreplace.la \ $(top_builddir)/lib/pils/libpils.la libplumb_la_LDFLAGS = -version-info 3:0:1 libplumbgpl_la_SOURCES = setproctitle.c libplumbgpl_la_LIBADD = $(top_builddir)/replace/libreplace.la \ $(top_builddir)/lib/pils/libpils.la libplumbgpl_la_LDFLAGS = -version-info 2:0:0 testdir = $(libdir)/@HB_PKG@ test_PROGRAMS = ipctest ipctransientclient ipctransientserver base64_md5_test test_SCRIPTS = transient-test.sh ipctest_SOURCES = ipctest.c ipctest_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \ $(top_builddir)/lib/pils/libpils.la noinst_HEADERS = ipctransient.h #ipctransient_SOURCES = ipctransient.c #ipctransient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(top_builddir)/heartbeat/libhbclient.la $(GLIBLIB) ipctransientclient_SOURCES = ipctransientclient.c ipctransientlib.c ipctransientclient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \ $(top_builddir)/lib/pils/libpils.la ipctransientserver_SOURCES = ipctransientserver.c ipctransientlib.c ipctransientserver_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \ $(top_builddir)/lib/pils/libpils.la #netstring_test_SOURCES = netstring_test.c #netstring_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la libhbclient.la $(gliblib) base64_md5_test_SOURCES = base64_md5_test.c base64_md5_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) EXTRA_DIST = $(test_SCRIPTS) Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/base64.c0000644000000000000000000002316012120057602025415 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "clplumbing/base64.h" /* * * Base64 conversion functions. * They convert from a binary array into a single string * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341 * The only difference is that we don't care about line lengths. * We do use their encoding algorithm. * */ static char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define EQUALS '=' #define MASK6 (077) #define MASK24 (077777777) /* Convert from binary to a base64 string (~ according to RFC1341) */ int binary_to_base64(const void * data, int nbytes, char * output, int outlen) { int requiredlen = B64_stringlen(nbytes)+1; /* EOS */ char * outptr; const unsigned char * inmax; const unsigned char * inlast; const unsigned char * inptr; int bytesleft; if (outlen < requiredlen) { syslog(LOG_ERR, "binary_to_base64: output area too small."); return -1; } inptr = data; /* Location of last whole 3-byte chunk */ inmax = inptr + ((nbytes / B64inunit)*B64inunit); inlast = inptr + nbytes; outptr = output; /* Convert whole 3-byte chunks */ for (;inptr < inmax; inptr += B64inunit) { unsigned long chunk; unsigned int sixbits; chunk = ((*inptr) << 16 | ((*(inptr+1)) << 8) | (*(inptr+2))) & MASK24; sixbits = (chunk >> 18) & MASK6; *outptr = b64chars[sixbits]; ++outptr; sixbits = (chunk >> 12) & MASK6; *outptr = b64chars[sixbits]; ++outptr; sixbits = (chunk >> 6) & MASK6; *outptr = b64chars[sixbits]; ++outptr; sixbits = (chunk & MASK6); *outptr = b64chars[sixbits]; ++outptr; } /* Do we have anything left over? */ bytesleft = inlast - inptr; if (bytesleft > 0) { /* bytesleft can only be 1 or 2 */ unsigned long chunk; unsigned int sixbits; /* Grab first byte */ chunk = (*inptr) << 16; if (bytesleft == 2) { /* Grab second byte */ chunk |= ((*(inptr+1)) << 8); } chunk &= MASK24; /* OK, now we have our chunk... */ sixbits = (chunk >> 18) & MASK6; *outptr = b64chars[sixbits]; ++outptr; sixbits = (chunk >> 12) & MASK6; *outptr = b64chars[sixbits]; ++outptr; if (bytesleft == 2) { sixbits = (chunk >> 6) & MASK6; *outptr = b64chars[sixbits]; }else{ *outptr = EQUALS; } ++outptr; *outptr = EQUALS; ++outptr; } *outptr = EOS; /* Don't increment */ return (outptr - output); } /* This macro is only usable by the base64_to_binary() function. * * There are faster ways of doing this, but I didn't spend the time * to implement one of them. If we have an array of six bit values, * sized by 256 or so, then we could look it up. * FIXME: This is how it really ought to be done... */ static unsigned char b64values [256]; #define BADVALUE 0xff static void init_b64_values(void) { int j; memset(b64values, BADVALUE, sizeof(b64values)); for (j=0; b64chars[j] != EOS; ++j) { b64values[(int)b64chars[j]] = (unsigned char)j; } } #define Char2SixBits(in, out) { \ unsigned char ch; \ ch = b64values[(unsigned int)in]; \ if (ch == BADVALUE) { \ syslog(LOG_ERR \ , "base64_to_binary: invalid input [%c]!" \ , in); \ return -1; \ } \ out = ch; \ } \ /* Convert from a base64 string (~ according to RFC1341) to binary */ int base64_to_binary(const char * in, int inlen, void * output, int outlen) { int maxbinlen = B64_maxbytelen(inlen); /* Worst case size */ const char * input = in; const char * lastinput = in + inlen - B64outunit; int equalcount = 0; unsigned char * startout; unsigned char * out; unsigned sixbits1; unsigned sixbits2; unsigned sixbits3; unsigned sixbits4; unsigned long chunk; static int inityet = 0; if (!inityet) { inityet=1; init_b64_values(); } /* Make sure we have enough room */ if (outlen < maxbinlen) { int residue = maxbinlen - outlen; if (residue > 2 || input[inlen-1] != EQUALS || (residue == 2 && input[inlen-2] != EQUALS)) { syslog(LOG_ERR , "base64_to_binary: output area too small."); return -1; } } if ((inlen % 4) != 0) { syslog(LOG_ERR , "base64_to_binary: input length invalid."); return -1; } if (inlen == 0) { return 0; } /* We have enough space. We are happy :-) */ startout = out = (unsigned char *)output; while (input < lastinput) { Char2SixBits(*input, sixbits1); ++input; Char2SixBits(*input, sixbits2); ++input; Char2SixBits(*input, sixbits3); ++input; Char2SixBits(*input, sixbits4); ++input; chunk = (sixbits1 << 18) | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4; *out = ((chunk >> 16) & 0xff); ++out; *out = ((chunk >> 8) & 0xff); ++out; *out = (chunk & 0xff); ++out; } /* Process last 4 chars of input (1 to 3 bytes of output) */ /* The first two input chars must be normal chars */ Char2SixBits(*input, sixbits1); ++input; Char2SixBits(*input, sixbits2); ++input; /* We should find one of: (char,char) (char,=) or (=,=) */ /* We then output: (3 bytes) (2 bytes) (1 byte) */ if (*input == EQUALS) { /* The (=,=): 1 byte case */ equalcount=2; sixbits3 = 0; sixbits4 = 0; /* We assume the 2nd char is an = sign :-) */ }else{ /* We have either the (char,char) or (char,=) case */ Char2SixBits(*input, sixbits3); ++input; if (*input == EQUALS) { /* The (char, =): 2 bytes case */ equalcount=1; sixbits4 = 0; }else{ /* The (char, char): 3 bytes case */ Char2SixBits(*input, sixbits4); ++input; equalcount=0; } } chunk = (sixbits1 << 18) | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4; /* We always have one more char to output... */ *out = ((chunk >> 16) & 0xff); ++out; if (equalcount < 2) { /* Zero or one equal signs: total of 2 or 3 bytes output */ *out = ((chunk >> 8) & 0xff); ++out; if (equalcount < 1) { /* No equal signs: total of 3 bytes output */ *out = (chunk & 0xff); ++out; } } return out - startout; } #if 0 #define RAND(upb) (rand()%(upb)) void dumpbin(void * Bin, int length); void randbin(void * Bin, int length); void dumpbin(void * Bin, int length) { unsigned char * bin = Bin; int j; for (j=0; j < length; ++j) { fprintf(stderr, "%02x ", bin[j]); if ((j % 32) == 31) { fprintf(stderr, "\n"); } } fprintf(stderr, "\n"); } void randbin(void * Bin, int length) { unsigned char * bin = Bin; int j; for (j=0; j < length; ++j) { bin[j] = (unsigned char)RAND(256); } } #define MAXLEN 320 #define MAXSTRING B64_stringlen(MAXLEN)+1 #define MAXITER 300000 int main(int argc, char ** argv) { int errcount = 0; char origbin[MAXLEN+1]; char sourcebin[MAXLEN+1]; char destbin[MAXLEN+1]; char deststr[MAXSTRING]; int maxiter = MAXITER; int j; for (j=0; j < maxiter; ++j) { int iterlen = RAND(MAXLEN+1); int slen; int blen; if ((j%100) == 99) { fprintf(stderr, "+"); } memset(origbin, 0, MAXLEN+1); memset(sourcebin, 0, MAXLEN+1); memset(destbin, 0, MAXLEN+1); randbin(origbin, iterlen); origbin[iterlen] = 0x1; memcpy(sourcebin, origbin, iterlen); sourcebin[iterlen] = 0x2; slen = binary_to_base64(sourcebin, iterlen, deststr, MAXSTRING); if (slen < 0) { fprintf(stderr , "binary_to_base64 failure: length %d\n" , iterlen); ++errcount; continue; } if (strlen(deststr) != slen) { fprintf(stderr , "binary_to_base64 failure: length was %d (strlen) vs %d (ret value)\n" , strlen(deststr), slen); fprintf(stderr, "binlen: %d, deststr: [%s]\n" , iterlen, deststr); continue; ++errcount; } destbin[iterlen] = 0x3; blen = base64_to_binary(deststr, slen, destbin, iterlen); if (blen != iterlen) { fprintf(stderr , "base64_to_binary failure: length was %d vs %d\n" , blen, iterlen); dumpbin(origbin, iterlen); fprintf(stderr , "Base64 intermediate: [%s]\n", deststr); ++errcount; continue; } if (memcmp(destbin, origbin, iterlen) != 0) { fprintf(stderr , "base64_to_binary mismatch. Orig:\n"); dumpbin(origbin, iterlen); fprintf(stderr, "Dest:\n"); dumpbin(destbin, iterlen); fprintf(stderr , "Base64 intermediate: [%s]\n", deststr); ++errcount; } if (destbin[iterlen] != 0x3) { fprintf(stderr , "base64_to_binary corruption. dest byte: 0x%02x\n" , destbin[iterlen]); ++errcount; } if (sourcebin[iterlen] != 0x2) { fprintf(stderr , "base64_to_binary corruption. source byte: 0x%02x\n" , sourcebin[iterlen]); ++errcount; } sourcebin[iterlen] = 0x0; origbin[iterlen] = 0x0; if (memcmp(sourcebin, origbin, MAXLEN+1) != 0) { fprintf(stderr , "base64_to_binary corruption. origbin:\n"); dumpbin(origbin, MAXLEN+1); fprintf(stderr, "sourcebin:\n"); dumpbin(sourcebin, MAXLEN+1); ++errcount; } } fprintf(stderr, "\n%d iterations, %d errors.\n" , maxiter, errcount); return errcount; } /* HA-logging function */ void ha_log(int priority, const char * fmt, ...) { va_list ap; char buf[MAXLINE]; va_start(ap, fmt); vsnprintf(buf, MAXLINE, fmt, ap); va_end(ap); fprintf(stderr, "%s\n", buf); } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/base64_md5_test.c0000644000000000000000000000663212120057602027226 0ustar00usergroup00000000000000/* File: base64_md5_test.c * Description: base64 and md5 algorithm tests * * Author: Sun Jiang Dong * Copyright (c) 2005 International Business Machines * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define MD5LEN 16 /* md5 buffer */ #define BASE64_BUF_LEN 32 /* gcc -o base64_md5_test base64_md5_test.c -lplumb */ int main(void) { int error_count = 0; const char base64_encode[] = "YWJjZGVmZ2g="; const char raw_data[] = "abcdefgh"; /* A test case from * RFC 1321 - The MD5 Message-Digest Algorithm */ const char * data1 = "message digest"; const char digest_rfc1321[(MD5LEN+1)*2+1] = "f96b697d7cb7938d525a2f31aaf161d0"; /* A test case from * RFC 2104 - HMAC: Keyed-Hashing for Message Authentication */ const char *key = "Jefe"; const char *data2 = "what do ya want for nothing?"; const char digest_rfc2104[(MD5LEN+1)*2+1] = "750c783e6ab0b503eaa86e310a5db738"; char buffer_tmp[BASE64_BUF_LEN]; char md[(MD5LEN+1)*2+1]; unsigned char digest[MD5LEN]; char * md_tmp; int rc,i; /* base64 encode test */ binary_to_base64(raw_data, strlen(raw_data), buffer_tmp , BASE64_BUF_LEN); /* printf("base64_encode = %s\n", buffer_tmp); */ if (0 != strncmp(buffer_tmp, base64_encode, strlen(buffer_tmp)) ) { cl_log(LOG_ERR, "binary_to_base64 works bad."); error_count++; } /* base64 decode test */ memset(buffer_tmp, 0, BASE64_BUF_LEN); base64_to_binary(base64_encode, strlen(base64_encode) , buffer_tmp, BASE64_BUF_LEN); /* printf("decoded data= %s\n", buffer_tmp); */ if (0 != strncmp(buffer_tmp, raw_data, strlen(buffer_tmp)) ) { cl_log(LOG_ERR, "base64_to_binary works bad."); error_count++; } rc = MD5((const unsigned char *)data1, strlen(data1), digest); md_tmp = md; for (i = 0; i < MD5LEN; i++) { snprintf(md_tmp, sizeof(md), "%02x", digest[i]); md_tmp += 2; } *md_tmp = '\0'; /* printf("rc=%d MD5=%s\n", rc, md); */ if (0 != strncmp(md, digest_rfc1321, MD5LEN*2) ) { cl_log(LOG_ERR, "The md5-rfc1321 algorithm works bad."); error_count++; } rc = HMAC((const unsigned char *)key, strlen(key) , (const unsigned char *)data2, strlen(data2), digest); md_tmp = md; for (i = 0; i < MD5LEN; i++) { sprintf(md_tmp,"%02x", digest[i]); md_tmp += 2; } *md_tmp = '\0'; /* printf("rc=%d HMAC=%s\n", rc, md); */ if (0 != strncmp(md, digest_rfc2104, MD5LEN*2) ) { cl_log(LOG_ERR, "The md5-rfc2104 algorithm works bad."); error_count++; } (void) rc; /* Suppress -Werror=unused-but-set-variable */ return error_count; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_compress.c0000644000000000000000000002720312120057602026644 0ustar00usergroup00000000000000 /* * compress.c: Compression functions for Linux-HA * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Compression is designed to handle big messages, right now with 4 nodes * cib message can go up to 64 KB or more. I expect much larger messages * when the number of node increase. This makes message compression necessary. * * * Compression is handled in field level. One can add a struct field using * ha_msg_addstruct() -- the field will not get compressed, or using * ha_msg_addstruct_compress(), and the field will get compressed when * the message is converted to wire format, i.e. when msg2wirefmt() is called. * The compressed field will stay compressed until it reached the desination. * It will finally decompressed when the user start to get the field value. * It is designed this way so that the compression/decompression only happens * in end users so that heartbeat itself can save cpu cycle and memory. * (more info about compression can be found in cl_msg_types.c about FT_COMPRESS * FT_UNCOMPRESS types) * * compression has another legacy mode, which is there so it can be compatible * to old ways of compression. In the old way, no field is compressed individually * and the messages is compressed before it is sent out, and it will be decompressed * in the receiver side immediately. So in each IPC channel, the message is compressed * and decompressed once. This way will cost a lot of cpu time and memory and it is * discouraged. * * If use_traditional_compression is true, then it is using the legacy mode, otherwise * it is using the new compression. For back compatibility, the default is legacy mode. * * The real compression work is done by compression plugins. There are two plugins right * now: zlib and bz2, they are in lib/plugins/compress * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define COMPRESSED_FIELD "_compressed_payload" #define COMPRESS_NAME "_compression_algorithm" #define HACOMPRESSNAME "HA_COMPRESSION" #define DFLT_COMPRESS_PLUGIN "bz2" static struct hb_compress_fns* msg_compress_fns = NULL; static char* compress_name = NULL; GHashTable* CompressFuncs = NULL; static PILGenericIfMgmtRqst Reqs[] = { {"compress", &CompressFuncs, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; static PILPluginUniv* CompressPIsys = NULL; static int init_pluginsys(void){ if (CompressPIsys) { return TRUE; } CompressPIsys = NewPILPluginUniv(HA_PLUGIN_DIR); if (CompressPIsys) { if (PILLoadPlugin(CompressPIsys, PI_IFMANAGER, "generic", Reqs) != PIL_OK){ cl_log(LOG_ERR, "generic plugin load failed\n"); DelPILPluginUniv(CompressPIsys); CompressPIsys = NULL; } }else{ cl_log(LOG_ERR, "pi univ creation failed\n"); } return CompressPIsys != NULL; } int cl_compress_remove_plugin(const char* pluginname) { return HA_OK; } int cl_compress_load_plugin(const char* pluginname) { struct hb_compress_fns* funcs = NULL; if (!init_pluginsys()){ return HA_FAIL; } if ((funcs = g_hash_table_lookup(CompressFuncs, pluginname)) == NULL){ if (PILPluginExists(CompressPIsys, HB_COMPRESS_TYPE_S, pluginname) == PIL_OK){ PIL_rc rc; if ((rc = PILLoadPlugin(CompressPIsys, HB_COMPRESS_TYPE_S, pluginname, NULL))!= PIL_OK){ cl_log(LOG_ERR, "Cannot load compress plugin %s[%s]", pluginname, PIL_strerror(rc)); return HA_FAIL; } funcs = g_hash_table_lookup(CompressFuncs, pluginname); } } if (funcs == NULL){ cl_log(LOG_ERR, "Compression module(%s) not found", pluginname); return HA_FAIL; } /* set the environment variable so that later programs can * load the appropriate plugin */ setenv(HACOMPRESSNAME,pluginname,1); msg_compress_fns = funcs; return HA_OK; } int cl_set_compress_fns(const char* pluginname) { /* this function was unnecessary duplication of the * code in cl_compress_load_plugin */ return cl_compress_load_plugin(pluginname); } struct hb_compress_fns* cl_get_compress_fns(void) { static int try_dflt = 1; if (try_dflt && !msg_compress_fns) { try_dflt = 0; cl_log(LOG_INFO, "%s: user didn't set compression type, " "loading %s plugin", __FUNCTION__, DFLT_COMPRESS_PLUGIN); cl_compress_load_plugin(DFLT_COMPRESS_PLUGIN); } return msg_compress_fns; } static struct hb_compress_fns* get_compress_fns(const char* pluginname) { struct hb_compress_fns* funcs = NULL; if (cl_compress_load_plugin(pluginname) != HA_OK){ cl_log(LOG_ERR, "%s: loading compression module" "(%s) failed", __FUNCTION__, pluginname); return NULL; } funcs = g_hash_table_lookup(CompressFuncs, pluginname); return funcs; } void cl_realtime_malloc_check(void); char* cl_compressmsg(struct ha_msg* m, size_t* len) { char* src; char* dest; size_t destlen; int rc; char* ret = NULL; struct ha_msg* tmpmsg; size_t datalen; destlen = MAXMSG; dest = malloc(destlen); if (!dest) { cl_log(LOG_ERR, "%s: failed to allocate destination buffer", __FUNCTION__); return NULL; } if (msg_compress_fns == NULL){ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!", __FUNCTION__); goto out; } if ( get_netstringlen(m) > MAXUNCOMPRESSED || get_stringlen(m) > MAXUNCOMPRESSED){ cl_log(LOG_ERR, "%s: msg too big(stringlen=%d," "netstringlen=%d)", __FUNCTION__, get_stringlen(m), get_netstringlen(m)); goto out; } if ((src = msg2wirefmt_noac(m, &datalen)) == NULL){ cl_log(LOG_ERR,"%s: converting msg" " to wirefmt failed", __FUNCTION__); goto out; } rc = msg_compress_fns->compress(dest, &destlen, src, datalen); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: compression failed", __FUNCTION__); goto out; } free(src); tmpmsg =ha_msg_new(0); rc = ha_msg_addbin(tmpmsg, COMPRESSED_FIELD, dest, destlen)/*discouraged function*/; if (rc != HA_OK){ cl_log(LOG_ERR, "%s: adding binary to msg failed", __FUNCTION__); goto out; } rc = ha_msg_add(tmpmsg, COMPRESS_NAME, msg_compress_fns->getname()); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: adding compress name to msg failed", __FUNCTION__); goto out; } ret = msg2netstring(tmpmsg, len); ha_msg_del(tmpmsg); #if 0 cl_log(LOG_INFO, "------original stringlen=%d, netstringlen=%d," "compressed_datalen=%d,current len=%d", get_stringlen(m), get_netstringlen(m),(int)destlen, (int)*len); #endif out: if (dest) { free(dest); } return ret; } gboolean is_compressed_msg(struct ha_msg* m) { if( cl_get_binary(m, COMPRESSED_FIELD, NULL) /*discouraged function*/ != NULL){ return TRUE; } return FALSE; } /* the decompressmsg function is not exactly the reverse * operation of compressmsg, it starts when the prorgram * detects there is compressed_field in a msg */ struct ha_msg* cl_decompressmsg(struct ha_msg* m) { const char* src; size_t srclen; char *dest = NULL; size_t destlen = MAXUNCOMPRESSED; int rc; struct ha_msg* ret = NULL; const char* decompress_name; struct hb_compress_fns* funcs = NULL; dest = malloc(destlen); if (!dest) { cl_log(LOG_ERR, "%s: Failed to allocate buffer.", __FUNCTION__); return NULL; } if (m == NULL){ cl_log(LOG_ERR, "%s: NULL message", __FUNCTION__); goto out; } src = cl_get_binary(m, COMPRESSED_FIELD, &srclen)/*discouraged function*/; if (src == NULL){ cl_log(LOG_ERR, "%s: compressed-field is NULL", __FUNCTION__); goto out; } if (srclen > MAXMSG){ cl_log(LOG_ERR, "%s: field too long(%d)", __FUNCTION__, (int)srclen); goto out; } decompress_name = ha_msg_value(m, COMPRESS_NAME); if (decompress_name == NULL){ cl_log(LOG_ERR, "compress name not found"); goto out; } funcs = get_compress_fns(decompress_name); if (funcs == NULL){ cl_log(LOG_ERR, "%s: compress method(%s) is not" " supported in this machine", __FUNCTION__, decompress_name); goto out; } rc = funcs->decompress(dest, &destlen, src, srclen); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: decompression failed", __FUNCTION__); goto out; } ret = wirefmt2msg(dest, destlen, 0); #if 0 cl_log(LOG_INFO, "%s: srclen =%d, destlen=%d", __FUNCTION__, srclen, destlen); #endif out: if (dest) { free(dest); } return ret; } int cl_decompress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen) { char* value; int vallen; int rc; const char* decompress_name; struct hb_compress_fns* funcs; if ( msg == NULL|| index >= msg->nfields){ cl_log(LOG_ERR, "%s: wrong argument", __FUNCTION__); return HA_FAIL; } value = msg->values[index]; vallen = msg->vlens[index]; decompress_name = ha_msg_value(msg, COMPRESS_NAME); if (decompress_name == NULL){ cl_log(LOG_ERR, "compress name not found"); return HA_FAIL; } funcs = get_compress_fns(decompress_name); if (funcs == NULL){ cl_log(LOG_ERR, "%s: compress method(%s) is not" " supported in this machine", __FUNCTION__, decompress_name); return HA_FAIL; } rc = funcs->decompress(buf, buflen, value, vallen); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: decompression failed", __FUNCTION__); return HA_FAIL; } return HA_OK; } int cl_compress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen) { char* src; size_t srclen; int rc; if ( msg == NULL|| index >= msg->nfields || msg->types[index] != FT_UNCOMPRESS){ cl_log(LOG_ERR, "%s: wrong argument", __FUNCTION__); return HA_FAIL; } if (msg_compress_fns == NULL){ if (compress_name == NULL){ compress_name = getenv(HACOMPRESSNAME); } if (compress_name == NULL){ cl_log(LOG_ERR, "%s: no compression module name found", __FUNCTION__); return HA_FAIL; } if(cl_set_compress_fns(compress_name) != HA_OK){ cl_log(LOG_ERR, "%s: loading compression module failed", __FUNCTION__); return HA_FAIL; } } if (msg_compress_fns == NULL){ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!", __FUNCTION__); return HA_FAIL; } src = msg2wirefmt_noac(msg->values[index], &srclen); if (src == NULL){ cl_log(LOG_ERR,"%s: converting msg" " to wirefmt failed", __FUNCTION__); return HA_FAIL; } rc = msg_compress_fns->compress(buf, buflen, src, srclen); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: compression failed", __FUNCTION__); return HA_FAIL; } rc = ha_msg_mod(msg, COMPRESS_NAME, msg_compress_fns->getname()); if (rc != HA_OK){ cl_log(LOG_ERR, "%s: adding compress name to msg failed", __FUNCTION__); return HA_FAIL;; } free(src); src = NULL; return HA_OK; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_log.c0000644000000000000000000007035512120057602025600 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAXLINE # define MAXLINE 512 #endif /* * might not contain LOG_PRI... * So, we define it ourselves, or error out if we can't... */ #ifndef LOG_PRI # ifdef LOG_PRIMASK /* David Lee reports this works on Solaris */ # define LOG_PRI(p) ((p) & LOG_PRIMASK) # else # error "Syslog.h does not define either LOG_PRI or LOG_PRIMASK." # endif #endif #define DFLT_ENTITY "cluster" #define DFLT_PREFIX "" #define NULLTIME 0 #define QUEUE_SATURATION_FUZZ 10 static IPC_Channel* logging_daemon_chan = NULL; static gboolean syslogformatfile = TRUE; /* * If true, then output messages more or less like this... * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null */ int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str); static int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str); static IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bstrlen, gboolean use_priority_str, IPC_Channel* ch); static void FreeChildLogIPCMessage(IPC_Message* msg); static gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan); static int cl_set_logging_wqueue_maxlen(int qlen); static int use_logging_daemon = FALSE; static int conn_logd_time = 0; static char cl_log_entity[MAXENTITY]= DFLT_ENTITY; static char cl_log_syslogprefix[MAXENTITY] = DFLT_PREFIX; static char common_log_entity[MAXENTITY]= DFLT_ENTITY; static int cl_log_facility = LOG_USER; static int use_buffered_io = 0; static void cl_opensyslog(void); static int syslog_enabled = 0; static int stderr_enabled = 0; static int stdout_enabled = 0; static const char* logfile_name = NULL; static const char* debugfile_name = NULL; static int cl_process_pid = -1; int debug_level = 0; static GDestroyNotify destroy_logging_channel_callback; static void (*create_logging_channel_callback)(IPC_Channel* chan); static gboolean logging_chan_in_main_loop = FALSE; /*********************** *debug use only, do not use this function in your program */ IPC_Channel * get_log_chan(void); IPC_Channel* get_log_chan(void){ return logging_daemon_chan; } /*************************/ /************************** * check if the fd is in use for logging **************************/ int cl_log_is_logd_fd(int fd) { return logging_daemon_chan && ( fd == logging_daemon_chan->ops->get_send_select_fd(logging_daemon_chan) || fd == logging_daemon_chan->ops->get_recv_select_fd(logging_daemon_chan) ); } void cl_log_enable_stderr(int truefalse) { stderr_enabled = truefalse; } void cl_log_enable_stdout(int truefalse) { stdout_enabled = truefalse; } void cl_log_set_uselogd(int truefalse) { use_logging_daemon = truefalse; } void cl_log_enable_syslog_filefmt(int truefalse) { syslogformatfile = (gboolean)truefalse; } gboolean cl_log_get_uselogd(void) { return use_logging_daemon; } int cl_log_get_logdtime(void) { return conn_logd_time; } void cl_log_set_logdtime(int logdtime) { conn_logd_time = logdtime; return; } void cl_log_use_buffered_io(int truefalse) { use_buffered_io = truefalse; cl_log_close_log_files(); } #define ENVPRE "HA_" #define ENV_HADEBUGVAL "HA_debug" #define ENV_LOGFENV "HA_logfile" /* well-formed log file :-) */ #define ENV_DEBUGFENV "HA_debugfile" /* Debug log file */ #define ENV_LOGFACILITY "HA_logfacility"/* Facility to use for logger */ #define ENV_SYSLOGFMT "HA_syslogmsgfmt"/* TRUE if we should use syslog message formatting */ #define ENV_LOGDAEMON "HA_use_logd" #define ENV_CONNINTVAL "HA_conn_logd_time" #define TRADITIONAL_COMPRESSION "HA_traditional_compression" #define COMPRESSION "HA_compression" static void inherit_compress(void) { char* inherit_env = NULL; inherit_env = getenv(TRADITIONAL_COMPRESSION); if (inherit_env != NULL && *inherit_env != EOS) { gboolean value; if (cl_str_to_boolean(inherit_env, &value)!= HA_OK){ cl_log(LOG_ERR, "inherit traditional_compression failed"); }else{ cl_set_traditional_compression(value); } } } void cl_inherit_logging_environment(int logqueuemax) { char * inherit_env = NULL; /* Donnot need to free the return pointer from getenv */ inherit_env = getenv(ENV_HADEBUGVAL); if (inherit_env != NULL && atoi(inherit_env) != 0 ) { debug_level = atoi(inherit_env); inherit_env = NULL; } inherit_env = getenv(ENV_LOGFENV); if (inherit_env != NULL && *inherit_env != EOS) { cl_log_set_logfile(inherit_env); inherit_env = NULL; } inherit_env = getenv(ENV_DEBUGFENV); if (inherit_env != NULL && *inherit_env != EOS) { cl_log_set_debugfile(inherit_env); inherit_env = NULL; } inherit_env = getenv(ENV_LOGFACILITY); if (inherit_env != NULL && *inherit_env != EOS) { int facility = -1; facility = cl_syslogfac_str2int(inherit_env); if ( facility >= 0 ) { cl_log_set_facility(facility); } inherit_env = NULL; } inherit_env = getenv(ENV_SYSLOGFMT); if (inherit_env != NULL && *inherit_env != EOS) { gboolean truefalse; if (cl_str_to_boolean(inherit_env, &truefalse) == HA_OK) { cl_log_enable_syslog_filefmt(truefalse); } } inherit_env = getenv(ENV_LOGDAEMON); if (inherit_env != NULL && *inherit_env != EOS) { gboolean uselogd; cl_str_to_boolean(inherit_env, &uselogd); cl_log_set_uselogd(uselogd); if (uselogd) { if (logqueuemax > 0) { cl_set_logging_wqueue_maxlen(logqueuemax); } } } inherit_env = getenv(ENV_CONNINTVAL); if (inherit_env != NULL && *inherit_env != EOS) { int logdtime; logdtime = cl_get_msec(inherit_env); cl_log_set_logdtime(logdtime); } inherit_compress(); return; } static void add_logging_channel_mainloop(IPC_Channel* chan) { GCHSource* chp= G_main_add_IPC_Channel( G_PRIORITY_DEFAULT, chan, FALSE, NULL, NULL, destroy_logging_channel_callback); if (chp == NULL){ cl_log(LOG_INFO, "adding logging channel to mainloop failed"); } logging_chan_in_main_loop = TRUE; return; } static void remove_logging_channel_mainloop(gpointer userdata) { logging_chan_in_main_loop = FALSE; return; } static IPC_Channel* create_logging_channel(void) { GHashTable* attrs; char path[] = IPC_PATH_ATTR; char sockpath[] = HA_LOGDAEMON_IPC; IPC_Channel* chan; static gboolean complained_yet = FALSE; attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(attrs, path, sockpath); chan =ipc_channel_constructor(IPC_ANYTYPE, attrs); g_hash_table_destroy(attrs); if (chan == NULL) { cl_log(LOG_ERR, "create_logging_channel:" "contructing ipc channel failed"); return NULL; } if (chan->ops->initiate_connection(chan) != IPC_OK) { if (!complained_yet) { complained_yet = TRUE; cl_log(LOG_WARNING, "Initializing connection" " to logging daemon failed." " Logging daemon may not be running"); } if (!logging_chan_in_main_loop){ chan->ops->destroy(chan); } return NULL; } complained_yet = FALSE; if (create_logging_channel_callback){ create_logging_channel_callback(chan); } return chan; } gboolean cl_log_test_logd(void) { IPC_Channel* chan = logging_daemon_chan; if (chan && chan->ops->get_chan_status(chan) == IPC_CONNECT){ return TRUE; } if (chan ){ if (!logging_chan_in_main_loop){ chan->ops->destroy(chan); } logging_daemon_chan = chan = NULL; } logging_daemon_chan = chan = create_logging_channel(); if (chan == NULL){ return FALSE; } if(chan->ops->get_chan_status(chan) != IPC_CONNECT){ if (!logging_chan_in_main_loop){ chan->ops->destroy(chan); } logging_daemon_chan = chan = NULL; return FALSE; } return TRUE; } /* FIXME: This is way too ugly to bear */ void cl_log_set_facility(int facility) { if (syslog_enabled && facility == cl_log_facility) { return; } cl_log_facility = facility; closelog(); syslog_enabled = 0; if (facility > 0) { cl_opensyslog(); } } void cl_log_set_entity(const char * entity) { if (entity == NULL) { entity = DFLT_ENTITY; } strncpy(cl_log_entity, entity, MAXENTITY); cl_log_entity[MAXENTITY-1] = '\0'; if (syslog_enabled) { syslog_enabled = 0; cl_opensyslog(); } } void cl_log_set_syslogprefix(const char *prefix) { if (prefix == NULL) { prefix = DFLT_PREFIX; } strncpy(cl_log_syslogprefix, prefix, MAXENTITY); cl_log_syslogprefix[MAXENTITY-1] = '\0'; if (syslog_enabled) { syslog_enabled = 0; cl_opensyslog(); } } void cl_log_set_logfile(const char * path) { if(path != NULL && strcasecmp("/dev/null", path) == 0) { path = NULL; } logfile_name = path; cl_log_close_log_files(); } void cl_log_set_debugfile(const char * path) { if(path != NULL && strcasecmp("/dev/null", path) == 0) { path = NULL; } debugfile_name = path; cl_log_close_log_files(); } /* * This function sets two callback functions. * One for creating a channel and * the other for destroying a channel* */ int cl_log_set_logd_channel_source( void (*create_callback)(IPC_Channel* chan), GDestroyNotify destroy_callback) { IPC_Channel* chan = logging_daemon_chan ; if (destroy_callback == NULL){ destroy_logging_channel_callback = remove_logging_channel_mainloop; }else{ destroy_logging_channel_callback = destroy_callback; } if (create_callback == NULL){ create_logging_channel_callback = add_logging_channel_mainloop; }else{ create_logging_channel_callback = create_callback; } if (chan != NULL && chan->ops->get_chan_status(chan) == IPC_CONNECT){ add_logging_channel_mainloop(chan); } return 0; } const char * prio2str(int priority) { static const char *log_prio[8] = { "EMERG", "ALERT", "CRIT", "ERROR", "WARN", "notice", "info", "debug" }; int logpri; logpri = LOG_PRI(priority); return (logpri < 0 || logpri >= DIMOF(log_prio)) ? "(undef)" : log_prio[logpri]; } /* print log line to a FILE *f */ #define print_logline(fp,entity,entity_pid,ts,pristr,buf) { \ fprintf(fp, "%s[%d]: %s ",entity,entity_pid,ha_timestamp(ts)); \ if (pristr) \ fprintf(fp,"%s: %s\n",pristr,buf); \ else \ fprintf(fp,"%s\n",buf); \ } static char * syslog_timestamp(TIME_T t); static void cl_limit_log_update(struct msg_ctrl *ml, time_t ts); static void append_log(FILE * fp, const char * entity, int entity_pid , TIME_T timestamp, const char * pristr, const char * msg) { static int got_uname = FALSE; static struct utsname un; if (!syslogformatfile) { print_logline(fp, entity, entity_pid, timestamp, pristr, msg); return; } if (!got_uname) { uname(&un); } /* * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null */ fprintf(fp, "%s %s %s: [%d]: %s%s%s\n" , syslog_timestamp(timestamp) , un.nodename, entity, entity_pid , (pristr ? pristr : "") , (pristr ? ": " : "") , msg); } /* As performance optimization we try to keep the file descriptor * open all the time, but as logrotation needs to work, the calling * program actually needs a signal handler. * * To be able to keep files open even without signal handler, * we remember the stat info, and close/reopen if the inode changed. * We keep the number of stat() calls to one per file per minute. * logrotate should be configured for delayed compression, if any. */ struct log_file_context { FILE *fp; struct stat stat_buf; }; static struct log_file_context log_file, debug_file; static void close_log_file(struct log_file_context *lfc) { /* ignore errors, we cannot do anything about them anyways */ fflush(lfc->fp); fsync(fileno(lfc->fp)); fclose(lfc->fp); lfc->fp = NULL; } void cl_log_close_log_files(void) { if (log_file.fp) close_log_file(&log_file); if (debug_file.fp) close_log_file(&debug_file); } static void maybe_close_log_file(const char *fname, struct log_file_context *lfc) { struct stat buf; if (!lfc->fp) return; if (stat(fname, &buf) || buf.st_ino != lfc->stat_buf.st_ino) { close_log_file(lfc); cl_log(LOG_INFO, "log-rotate detected on logfile %s", fname); } } /* Default to unbuffered IO. logd or others can use cl_log_use_buffered_io(1) * to enable fully buffered mode, and then use fflush appropriately. */ static void open_log_file(const char *fname, struct log_file_context *lfc) { lfc->fp = fopen(fname ,"a"); if (!lfc->fp) { syslog(LOG_ERR, "Failed to open log file %s: %s\n" , fname, strerror(errno)); } else { setvbuf(lfc->fp, NULL, use_buffered_io ? _IOFBF : _IONBF, BUFSIZ); fstat(fileno(lfc->fp), &lfc->stat_buf); } } static void maybe_reopen_log_files(const char *log_fname, const char *debug_fname) { static TIME_T last_stat_time; if (log_file.fp || debug_file.fp) { TIME_T now = time(NULL); if (now - last_stat_time > 59) { /* Don't use an exact minute, have it jitter around a * bit against cron or others. Note that, if there * is no new log message, it can take much longer * than this to notice logrotation and actually close * our file handle on the possibly already rotated, * or even deleted. * * As long as at least one minute pases between * renaming the log file, and further processing, * no message will be lost, so this should do fine: * (mv ha-log ha-log.1; sleep 60; gzip ha-log.1) */ maybe_close_log_file(log_fname, &log_file); maybe_close_log_file(debug_fname, &debug_file); last_stat_time = now; } } if (log_fname && !log_file.fp) open_log_file(log_fname, &log_file); if (debug_fname && !debug_file.fp) open_log_file(debug_fname, &debug_file); } /* * This function can cost us realtime unless use_logging_daemon * is enabled. Then we log everything through a child process using * non-blocking IPC. */ /* Cluster logging function */ void cl_direct_log(int priority, const char* buf, gboolean use_priority_str, const char* entity, int entity_pid, TIME_T ts) { const char * pristr; int needprivs = !cl_have_full_privs(); pristr = use_priority_str ? prio2str(priority) : NULL; if (!entity) entity = *cl_log_entity ? cl_log_entity : DFLT_ENTITY; if (needprivs) { return_to_orig_privs(); } if (syslog_enabled) { snprintf(common_log_entity, MAXENTITY, "%s", *cl_log_syslogprefix ? cl_log_syslogprefix : entity); /* The extra trailing '\0' is supposed to work around some * "known syslog bug that ends up concatinating entries". * Knowledge about which syslog package, version, platform and * what exactly the bug was has been lost, but leaving it in * won't do any harm either. */ syslog(priority, "%s[%d]: %s%s%s%c", *cl_log_syslogprefix ? entity : "", entity_pid, pristr ?: "", pristr ? ": " : "", buf, 0); } maybe_reopen_log_files(logfile_name, debugfile_name); if (debug_file.fp) append_log(debug_file.fp, entity, entity_pid, ts, pristr, buf); if (priority != LOG_DEBUG && log_file.fp) append_log(log_file.fp, entity, entity_pid, ts, pristr, buf); if (needprivs) { return_to_dropped_privs(); } return; } void cl_log_do_fflush(int do_fsync) { if (log_file.fp) { fflush(log_file.fp); if (do_fsync) fsync(fileno(log_file.fp)); } if (debug_file.fp) { fflush(debug_file.fp); if (do_fsync) fsync(fileno(debug_file.fp)); } } /* * This function can cost us realtime unless use_logging_daemon * is enabled. Then we log everything through a child process using * non-blocking IPC. */ static int cl_log_depth = 0; /* Cluster logging function */ void cl_log(int priority, const char * fmt, ...) { va_list ap; char buf[MAXLINE]; ssize_t nbytes; cl_process_pid = (int)getpid(); cl_log_depth++; buf[MAXLINE-1] = EOS; va_start(ap, fmt); nbytes=vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (nbytes >= (ssize_t)sizeof(buf)){ nbytes = sizeof(buf) -1 ; } if (stderr_enabled) { print_logline(stderr, cl_log_entity,cl_process_pid, NULLTIME, prio2str(priority), buf); } if (stdout_enabled) { print_logline(stdout, cl_log_entity,cl_process_pid, NULLTIME, prio2str(priority), buf); } if (use_logging_daemon && cl_log_depth <= 1) { LogToLoggingDaemon(priority, buf, nbytes, TRUE); }else{ /* this may cause blocking... maybe should make it optional? */ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME); } cl_log_depth--; return; } /* * Log a message only if there were not too many messages of this * kind recently. This is too prevent log spamming in case a * condition persists over a long period of time. The maximum * number of messages for the timeframe and other details are * provided in struct logspam (see cl_log.h). * * Implementation details: * - max number of time_t slots is allocated; slots keep time * stamps of previous max number of messages * - we check if the difference between now (i.e. new message just * arrived) and the oldest message is _less_ than the window * timeframe * - it's up to the user to do cl_limit_log_new and afterwards * cl_limit_log_destroy, though the latter is usually not * necessary; the memory allocated with cl_limit_log_new stays * constant during the lifetime of the process * * NB on Thu Aug 4 15:26:49 CEST 2011: * This interface is very new, use with caution and report bugs. */ struct msg_ctrl * cl_limit_log_new(struct logspam *lspam) { struct msg_ctrl *ml; ml = (struct msg_ctrl *)malloc(sizeof(struct msg_ctrl)); if (!ml) { cl_log(LOG_ERR, "%s:%d: out of memory" , __FUNCTION__, __LINE__); return NULL; } ml->msg_slots = (time_t *)calloc(lspam->max, sizeof(time_t)); if (!ml->msg_slots) { cl_log(LOG_ERR, "%s:%d: out of memory" , __FUNCTION__, __LINE__); return NULL; } ml->lspam = lspam; cl_limit_log_reset(ml); return ml; /* to be passed later to cl_limit_log() */ } void cl_limit_log_destroy(struct msg_ctrl *ml) { if (!ml) return; g_free(ml->msg_slots); g_free(ml); } void cl_limit_log_reset(struct msg_ctrl *ml) { ml->last = -1; ml->cnt = 0; ml->suppress_t = (time_t)0; memset(ml->msg_slots, 0, ml->lspam->max * sizeof(time_t)); } static void cl_limit_log_update(struct msg_ctrl *ml, time_t ts) { ml->last = (ml->last + 1) % ml->lspam->max; *(ml->msg_slots + ml->last) = ts; if (ml->cnt < ml->lspam->max) ml->cnt++; } void cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...) { va_list ap; char buf[MAXLINE]; time_t last_ts, now = time(NULL); if (!ml) goto log_msg; if (ml->suppress_t) { if ((now - ml->suppress_t) < ml->lspam->reset_time) return; /* message blocking expired */ cl_limit_log_reset(ml); } last_ts = ml->last != -1 ? *(ml->msg_slots + ml->last) : (time_t)0; if ( ml->cnt < ml->lspam->max || /* not so many messages logged */ (now - last_ts) > ml->lspam->window /* messages far apart */ ) { cl_limit_log_update(ml, now); goto log_msg; } else { cl_log(LOG_INFO , "'%s' messages logged too often, " "suppressing messages of this kind for %ld seconds" , ml->lspam->id, ml->lspam->reset_time); cl_log(priority, "%s", ml->lspam->advice); ml->suppress_t = now; return; } log_msg: va_start(ap, fmt); vsnprintf(buf, MAXLINE, fmt, ap); va_end(ap); cl_log(priority, "%s", buf); } void cl_perror(const char * fmt, ...) { const char * err; va_list ap; char buf[MAXLINE]; err = strerror(errno); va_start(ap, fmt); vsnprintf(buf, MAXLINE, fmt, ap); va_end(ap); cl_log(LOG_ERR, "%s: %s", buf, err); } void cl_glib_msg_handler(const gchar *log_domain, GLogLevelFlags log_level , const gchar *message, gpointer user_data) { GLogLevelFlags level = (log_level & G_LOG_LEVEL_MASK); int ha_level; switch(level) { case G_LOG_LEVEL_ERROR: ha_level = LOG_ERR; break; case G_LOG_LEVEL_CRITICAL: ha_level = LOG_ERR; break; case G_LOG_LEVEL_WARNING: ha_level = LOG_WARNING; break; case G_LOG_LEVEL_MESSAGE: ha_level = LOG_NOTICE; break; case G_LOG_LEVEL_INFO: ha_level = LOG_INFO; break; case G_LOG_LEVEL_DEBUG: ha_level = LOG_DEBUG; break; default: ha_level = LOG_WARNING; break; } cl_log(ha_level, "glib: %s", message); } static char * syslog_timestamp(TIME_T t) { static char ts[64]; struct tm* ttm; TIME_T now; time_t nowtt; static const char* monthstr [12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* Work around various weridnesses in different OSes and time_t definitions */ if (t == 0){ now = time(NULL); }else{ now = t; } nowtt = (time_t)now; ttm = localtime(&nowtt); snprintf(ts, sizeof(ts), "%3s %02d %02d:%02d:%02d" , monthstr[ttm->tm_mon], ttm->tm_mday , ttm->tm_hour, ttm->tm_min, ttm->tm_sec); return(ts); } char * ha_timestamp(TIME_T t) { static char ts[64]; struct tm* ttm; TIME_T now; time_t nowtt; /* Work around various weridnesses in different OSes and time_t definitions */ if (t == 0){ now = time(NULL); }else{ now = t; } nowtt = (time_t)now; ttm = localtime(&nowtt); snprintf(ts, sizeof(ts), "%04d/%02d/%02d_%02d:%02d:%02d" , ttm->tm_year+1900, ttm->tm_mon+1, ttm->tm_mday , ttm->tm_hour, ttm->tm_min, ttm->tm_sec); return(ts); } static int cl_set_logging_wqueue_maxlen(int qlen) { int sendrc; IPC_Channel* chan = logging_daemon_chan; if (chan == NULL){ chan = logging_daemon_chan = create_logging_channel(); } if (chan == NULL){ return HA_FAIL; } if (chan->ch_status != IPC_CONNECT){ cl_log(LOG_ERR, "cl_set_logging_wqueue_maxle:" "channel is not connected"); if (!logging_chan_in_main_loop){ chan->ops->destroy(chan); } logging_daemon_chan = NULL; return HA_FAIL; } sendrc = chan->ops->set_send_qlen(logging_daemon_chan, qlen); if (sendrc == IPC_OK) { return HA_OK; }else { return HA_FAIL; } } int LogToDaemon(int priority, const char * buf, int bufstrlen, gboolean use_pri_str) { int rc; cl_log_depth++; rc= LogToLoggingDaemon(priority, buf, bufstrlen, use_pri_str); cl_log_depth--; return rc; } static int drop_msg_num = 0; void cl_flush_logs(void) { if(logging_daemon_chan == NULL) { return; } logging_daemon_chan->ops->waitout(logging_daemon_chan); } static int LogToLoggingDaemon(int priority, const char * buf, int bufstrlen, gboolean use_pri_str) { IPC_Channel* chan = logging_daemon_chan; static longclock_t nexttime = 0; IPC_Message* msg; int sendrc = IPC_FAIL; int intval = conn_logd_time; /* make sure we don't hold file descriptors open * we don't intend to use again */ cl_log_close_log_files(); if (chan == NULL) { longclock_t lnow = time_longclock(); if (cmp_longclock(lnow, nexttime) >= 0){ nexttime = add_longclock( lnow, msto_longclock(intval)); logging_daemon_chan = chan = create_logging_channel(); } } if (chan == NULL){ cl_direct_log( priority, buf, TRUE, NULL, cl_process_pid, NULLTIME); return HA_FAIL; } msg = ChildLogIPCMessage(priority, buf, bufstrlen, use_pri_str, chan); if (msg == NULL) { drop_msg_num++; return HA_FAIL; } if (chan->ch_status == IPC_CONNECT){ if (chan->ops->is_sending_blocked(chan)) { chan->ops->resume_io(chan); } /* Make sure there is room for the drop message _and_ the * one we wish to log. Otherwise there is no point. * * Try to avoid bouncing on the limit by additionally * waiting until there is room for QUEUE_SATURATION_FUZZ * messages. */ if (drop_msg_num > 0 && chan->send_queue->current_qlen < (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ)) { /* have to send it this way so the order is correct */ send_dropped_message(use_pri_str, chan); } /* Don't log a debug message if we're * approaching the queue limit and already * dropped a message */ if (drop_msg_num == 0 || chan->send_queue->current_qlen < (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ) || priority != LOG_DEBUG ) { sendrc = chan->ops->send(chan, msg); } } if (sendrc == IPC_OK) { return HA_OK; } else { if (chan->ops->get_chan_status(chan) != IPC_CONNECT) { if (!logging_chan_in_main_loop){ chan->ops->destroy(chan); } logging_daemon_chan = NULL; cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME); if (drop_msg_num > 0){ /* Direct logging here is ok since we're * switching to that for everything * "for a while" */ cl_log(LOG_ERR, "cl_log: %d messages were dropped" " : channel destroyed", drop_msg_num); } drop_msg_num=0; FreeChildLogIPCMessage(msg); return HA_FAIL; } drop_msg_num++; } FreeChildLogIPCMessage(msg); return HA_FAIL; } static gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan) { int sendrc; char buf[64]; int buf_len = 0; IPC_Message *drop_msg = NULL; memset(buf, 0, 64); snprintf(buf, 64, "cl_log: %d messages were dropped", drop_msg_num); buf_len = strlen(buf)+1; drop_msg = ChildLogIPCMessage(LOG_ERR, buf, buf_len, use_pri_str, chan); if(drop_msg == NULL || drop_msg->msg_len == 0) { return FALSE; } sendrc = chan->ops->send(chan, drop_msg); if(sendrc == IPC_OK) { drop_msg_num = 0; }else{ FreeChildLogIPCMessage(drop_msg); } return sendrc == IPC_OK; } static IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bufstrlen, gboolean use_prio_str, IPC_Channel* ch) { IPC_Message* ret; LogDaemonMsgHdr logbuf; int msglen; char* bodybuf; if (ch->msgpad > MAX_MSGPAD){ cl_log(LOG_ERR, "ChildLogIPCMessage: invalid msgpad(%d)", ch->msgpad); return NULL; } ret = (IPC_Message*)malloc(sizeof(IPC_Message)); if (ret == NULL) { return ret; } memset(ret, 0, sizeof(IPC_Message)); /* Compute msg len: including room for the EOS byte */ msglen = sizeof(LogDaemonMsgHdr)+bufstrlen + 1; bodybuf = malloc(msglen + ch->msgpad); if (bodybuf == NULL) { free(ret); return NULL; } memset(bodybuf, 0, msglen + ch->msgpad); memset(&logbuf, 0, sizeof(logbuf)); logbuf.msgtype = LD_LOGIT; logbuf.facility = cl_log_facility; logbuf.priority = priority; logbuf.use_pri_str = use_prio_str; logbuf.entity_pid = getpid(); logbuf.timestamp = time(NULL); if (*cl_log_entity){ strncpy(logbuf.entity,cl_log_entity,MAXENTITY); }else { strncpy(logbuf.entity,DFLT_ENTITY,MAXENTITY); } logbuf.msglen = bufstrlen + 1; memcpy(bodybuf + ch->msgpad, &logbuf, sizeof(logbuf)); memcpy(bodybuf + ch->msgpad + sizeof(logbuf), buf, bufstrlen); ret->msg_len = msglen; ret->msg_buf = bodybuf; ret->msg_body = bodybuf + ch->msgpad; ret->msg_done = FreeChildLogIPCMessage; ret->msg_ch = ch; return ret; } static void FreeChildLogIPCMessage(IPC_Message* msg) { if (msg == NULL) { return; } memset(msg->msg_body, 0, msg->msg_len); free(msg->msg_buf); memset(msg, 0, sizeof (*msg)); free(msg); return; } static void cl_opensyslog(void) { if (*cl_log_entity == '\0' || cl_log_facility < 0) { return; } syslog_enabled = 1; strncpy(common_log_entity, cl_log_entity, MAXENTITY); openlog(common_log_entity, LOG_CONS, cl_log_facility); } void cl_log_args(int argc, char **argv) { int lpc = 0; int len = 0; int existing_len = 0; char *arg_string = NULL; if(argc == 0 || argv == NULL) { return; } for(;lpc < argc; lpc++) { if(argv[lpc] == NULL) { break; } len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */ if(arg_string) { existing_len = strlen(arg_string); } arg_string = realloc(arg_string, len + existing_len); sprintf(arg_string + existing_len, "%s ", argv[lpc]); } cl_log(LOG_INFO, "Invoked: %s", arg_string); free(arg_string); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_malloc.c0000644000000000000000000005764612120057602026276 0ustar00usergroup00000000000000/* * Copyright (C) 2000 Alan Robertson * * This software licensed under the GNU LGPL. * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define HA_MALLOC_ORIGINAL #include #include #include #include #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #include #include #ifndef BSD #ifdef HAVE_MALLOC_H # include #endif #endif #include #include #include #include #ifndef _CLPLUMBING_CLMALLOC_NATIVE_H static cl_mem_stats_t default_memstats; static volatile cl_mem_stats_t * memstats = &default_memstats; /* * Compile time malloc debugging switches: * * MARK_PRISTINE - puts known byte pattern in freed memory * Good at finding "use after free" cases * Cheap in memory, but expensive in CPU * * MAKE_GUARD - puts a known pattern *after* allocated memory * Good at finding overrun problems after the fact * Cheap in CPU, adds a few bytes to each malloc item * */ #define MARK_PRISTINE 1 /* Expensive in CPU time */ #undef MARK_PRISTINE #define MAKE_GUARD 1 /* Adds 'n' bytes memory - cheap in CPU*/ #define USE_ASSERTS 1 #define DUMPONERR 1 #define RETURN_TO_MALLOC 1 #undef RETURN_TO_MALLOC #ifndef DUMPONERR # define DUMPIFASKED() /* nothing */ #else # define DUMPIFASKED() {abort();} #endif /* * * Malloc wrapper functions * * I wrote these so we can better track memory leaks, etc. and verify * that the system is stable in terms of memory usage. * * For our purposes, these functions are a somewhat faster than using * malloc directly (although they use a bit more memory) * * The general strategy is loosely related to the buddy system, * except very simple, well-suited to our continuous running * nature, and the constancy of the requests and messages. * * We keep an array of linked lists, each for a different size * buffer. If we need a buffer larger than the largest one provided * by the list, we go directly to malloc. * * Otherwise, we keep return them to the appropriate linked list * when we're done with them, and reuse them from the list. * * We never coalesce buffers on our lists, and we never free them. * * It's very simple. We get usage stats. It makes me happy. */ #define HA_MALLOC_MAGIC 0xFEEDBEEFUL #define HA_FREE_MAGIC 0xDEADBEEFUL /* * We put a struct cl_mhdr in front of every malloc item. * This means each malloc item is at least 12 bytes bigger than it theoretically * needs to be. But, it allows this code to be fast and recognize * multiple free attempts, and memory corruption *before* the object * * It's probably possible to combine these fields a bit, * since bucket and reqsize are only needed for allocated items, * both are bounded in value, and fairly strong integrity checks apply * to them. But then we wouldn't be able to tell *quite* as reliably * if someone gave us an item to free that we didn't allocate... * * Could even make the bucket and reqsize objects into 16-bit ints... * * The idea of getting it all down into 32-bits of overhead is * an interesting thought... * * But some architectures have alignment constraints. For instance, sparc * requires that double-word accesses be aligned on double-word boundaries. * Thus if the requested space is bigger than a double-word, then cl_mhdr * should, for safety, be a double-word multiple (minimum 8bytes, 64bits). */ #ifdef HA_MALLOC_TRACK # define HA_MALLOC_OWNER 64 struct cl_bucket; #endif struct cl_mhdr { # ifdef HA_MALLOC_MAGIC unsigned long magic; /* Must match HA_*_MAGIC */ #endif # ifdef HA_MALLOC_TRACK char owner[HA_MALLOC_OWNER]; struct cl_bucket * left; struct cl_bucket * right; int dumped; longclock_t mtime; #endif size_t reqsize; int bucket; }; struct cl_bucket { struct cl_mhdr hdr; struct cl_bucket * next; }; #define NUMBUCKS 12 #define NOBUCKET (NUMBUCKS) static struct cl_bucket* cl_malloc_buckets[NUMBUCKS]; static size_t cl_bucket_sizes[NUMBUCKS]; static size_t buckminpow2 = 0L; static int cl_malloc_inityet = 0; static size_t cl_malloc_hdr_offset = sizeof(struct cl_mhdr); static void* cl_new_mem(size_t size, int numbuck); static void cl_malloc_init(void); static void cl_dump_item(const struct cl_bucket*b); #ifdef MARK_PRISTINE # define PRISTVALUE 0xff static int cl_check_is_pristine(const void* v, unsigned size); static void cl_mark_pristine(void* v, unsigned size); static int pristoff; #endif #define BHDR(p) ((struct cl_bucket*)(void*)(((char*)p)-cl_malloc_hdr_offset)) #define CBHDR(p) ((const struct cl_bucket*)(const void*)(((const char*)p)-cl_malloc_hdr_offset)) #define MEMORYSIZE(p)(CBHDR(p)->hdr.reqsize) #define MALLOCSIZE(allocsize) ((allocsize) + cl_malloc_hdr_offset + GUARDSIZE) #define MAXMALLOC (SIZE_MAX-(MALLOCSIZE(0)+1)) #ifdef MAKE_GUARD # define GUARDLEN 4 static const unsigned char cl_malloc_guard[] = #if GUARDLEN == 1 {0xA5}; #endif #if GUARDLEN == 2 {0x5A, 0xA5}; #endif #if GUARDLEN == 4 {0x5A, 0xA5, 0x5A, 0xA5}; #endif # define GUARDSIZE sizeof(cl_malloc_guard) # define ADD_GUARD(cp) (memcpy((((char*)cp)+MEMORYSIZE(cp)), cl_malloc_guard, sizeof(cl_malloc_guard))) # define GUARD_IS_OK(cp) (memcmp((((const char*)cp)+MEMORYSIZE(cp)), \ cl_malloc_guard, sizeof(cl_malloc_guard)) == 0) # define CHECK_GUARD_BYTES(cp, msg) { \ if (!GUARD_IS_OK(cp)) { \ cl_log(LOG_ERR, "%s: guard corrupted at 0x%lx", msg \ , (unsigned long)cp); \ cl_dump_item(CBHDR(cp)); \ DUMPIFASKED(); \ } \ } #else # define GUARDSIZE 0 # define ADD_GUARD(cp) /* */ # define GUARD_IS_OK(cp) (1) # define CHECK_GUARD_BYTES(cp, msg) /* */ #endif #define MALLOCROUND 4096 /* Round big mallocs up to a multiple of this size */ #ifdef HA_MALLOC_TRACK static struct cl_bucket * cl_malloc_track_root = NULL; static void cl_ptr_tag(void *ptr, const char *file, const char *function, const int line) { struct cl_bucket* bhdr = BHDR(ptr); snprintf(bhdr->hdr.owner, HA_MALLOC_OWNER, "%s:%s:%d", file, function, line); } static void cl_ptr_track(void *ptr) { struct cl_bucket* bhdr = BHDR(ptr); #if defined(USE_ASSERTS) g_assert(bhdr->hdr.left == NULL); g_assert(bhdr->hdr.right == NULL); g_assert((cl_malloc_track_root == NULL) || (cl_malloc_track_root->hdr.left == NULL)); #endif bhdr->hdr.dumped = 0; bhdr->hdr.mtime = time_longclock(); if (cl_malloc_track_root == NULL) { cl_malloc_track_root = bhdr; } else { bhdr->hdr.right = cl_malloc_track_root; cl_malloc_track_root->hdr.left = bhdr; cl_malloc_track_root = bhdr; } } static void cl_ptr_release(void *ptr) { struct cl_bucket* bhdr = BHDR(ptr); /* cl_log(LOG_DEBUG, "cl_free: Freeing memory belonging to %s" , bhdr->hdr.owner); */ #if defined(USE_ASSERTS) g_assert(cl_malloc_track_root != NULL); g_assert(cl_malloc_track_root->hdr.left == NULL); #endif if (bhdr->hdr.left != NULL) { bhdr->hdr.left->hdr.right=bhdr->hdr.right; } if (bhdr->hdr.right != NULL) { bhdr->hdr.right->hdr.left=bhdr->hdr.left; } if (cl_malloc_track_root == bhdr) { cl_malloc_track_root=bhdr->hdr.right; } bhdr->hdr.left = NULL; bhdr->hdr.right = NULL; } static void cl_ptr_init(void) { cl_malloc_track_root = NULL; } int cl_malloc_dump_allocated(int log_level, gboolean filter) { int lpc = 0; struct cl_bucket* cursor = cl_malloc_track_root; longclock_t time_diff; cl_log(LOG_INFO, "Dumping allocated memory buffers:"); while (cursor != NULL) { if(filter && cursor->hdr.dumped) { } else if(log_level > LOG_DEBUG) { } else if(filter) { lpc++; cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d" , cursor+cl_malloc_hdr_offset , cursor->hdr.owner , (int)cursor->hdr.reqsize); } else { lpc++; time_diff = sub_longclock(time_longclock(), cursor->hdr.mtime); cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d, dumped %d, age %lu ms" , cursor+cl_malloc_hdr_offset , cursor->hdr.owner , (int)cursor->hdr.reqsize , cursor->hdr.dumped , longclockto_long(time_diff)); } cursor->hdr.dumped = 1; cursor = cursor->hdr.right; } cl_log(LOG_INFO, "End dump."); return lpc; } #endif static const int LogTable256[] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; #define POW2BYTE(b) (LogTable256[b]) #define BYTE3(i) (((i)&0xFF000000)>>24) #define BYTE2(i) (((i)&0x00FF0000)>>16) #define BYTE1(i) (((i)&0x0000FF00)>>8) #define BYTE0(i) ((i)&0x000000FF) /* Works for malloc bucket sizes up to 2^8 */ #define POW21BYTE(i) (POW2BYTE(BYTE0(i))) /* Works for malloc bucket sizes up to 2^16 */ #define POW22BYTE(i) ((BYTE1(i) != 0x00)? (POW2BYTE(BYTE1(i))+8) \ : (POW21BYTE(i))) /* Works for malloc bucket sizes up to 2^24 */ #define POW23BYTE(i) ((BYTE2(i) != 0x00)? (POW2BYTE(BYTE2(i))+16) \ : POW22BYTE(i)) /* Works for malloc bucket sizes up to 2^32 */ #define POW24BYTE(i) ((BYTE3(i) != 0x00)? (POW2BYTE(BYTE3(i))+24) \ : POW23BYTE(i)) /* #define INT2POW2(i) POW24BYTE(i) / * This would allow 2G in our largest malloc chain */ /* which I don't think we need */ #define INT2POW2(i) POW23BYTE(i) /* This allows up to about 16 Mbytes in our largest malloc chain */ /* and it's a little faster than the one above */ /* * cl_malloc: malloc clone */ void * cl_malloc(size_t size) { #if 0 int j; #endif int numbuck = NOBUCKET; struct cl_bucket* buckptr = NULL; void* ret; if(!size) { cl_log(LOG_ERR , "%s: refusing to allocate zero sized block" , __FUNCTION__ ); return NULL; } if (size > MAXMALLOC) { return NULL; } if (!cl_malloc_inityet) { cl_malloc_init(); } #if 1 /* * NOTE: This restricts bucket sizes to be powers of two * - which is OK with me - and how the code has always worked :-D */ numbuck = INT2POW2(size-1)-buckminpow2; numbuck = MAX(0, numbuck); if (numbuck < NUMBUCKS) { if (size <= cl_bucket_sizes[numbuck] || (numbuck > 0 && size <= (cl_bucket_sizes[numbuck]/2))) { buckptr = cl_malloc_buckets[numbuck]; }else{ cl_log(LOG_ERR , "%s: bucket size bug: %lu bytes in %lu byte bucket #%d" , __FUNCTION__ , (unsigned long)size , (unsigned long)cl_bucket_sizes[numbuck] , numbuck); } } #else /* * Find which bucket would have buffers of the requested size */ for (j=0; j < NUMBUCKS; ++j) { if (size <= cl_bucket_sizes[j]) { numbuck = j; buckptr = cl_malloc_buckets[numbuck]; break; } } #endif /* * Pull it out of the linked list of free buffers if we can... */ if (buckptr == NULL) { ret = cl_new_mem(size, numbuck); }else{ cl_malloc_buckets[numbuck] = buckptr->next; buckptr->hdr.reqsize = size; ret = (((char*)buckptr)+cl_malloc_hdr_offset); #ifdef MARK_PRISTINE { int bucksize = cl_bucket_sizes[numbuck]; if (!cl_check_is_pristine(ret, bucksize)) { cl_log(LOG_ERR , "attempt to allocate memory" " which is not pristine."); cl_dump_item(buckptr); DUMPIFASKED(); } } #endif #ifdef HA_MALLOC_MAGIC switch (buckptr->hdr.magic) { case HA_FREE_MAGIC: break; case HA_MALLOC_MAGIC: cl_log(LOG_ERR , "attempt to allocate memory" " already allocated at 0x%lx" , (unsigned long)ret); cl_dump_item(buckptr); DUMPIFASKED(); ret=NULL; break; default: cl_log(LOG_ERR , "corrupt malloc buffer at 0x%lx" , (unsigned long)ret); cl_dump_item(buckptr); DUMPIFASKED(); ret=NULL; break; } buckptr->hdr.magic = HA_MALLOC_MAGIC; #endif /* HA_MALLOC_MAGIC */ if (memstats) { memstats->nbytes_req += size; memstats->nbytes_alloc += MALLOCSIZE(cl_bucket_sizes[numbuck]); } } if (ret && memstats) { #if 0 && defined(HAVE_MALLINFO) /* mallinfo is too expensive to use :-( */ struct mallinfo i = mallinfo(); memstats->arena = i.arena; #endif memstats->numalloc++; } if (ret) { #ifdef HA_MALLOC_TRACK /* If we were _always_ called via the wrapper functions, * this wouldn't be necessary, but we aren't, some use * function pointers directly to cl_malloc() */ cl_ptr_track(ret); cl_ptr_tag(ret, "cl_malloc.c", "cl_malloc", 0); #endif ADD_GUARD(ret); } return(ret); } int cl_is_allocated(const void *ptr) { #ifdef HA_MALLOC_MAGIC if (NULL == ptr || CBHDR(ptr)->hdr.magic != HA_MALLOC_MAGIC) { return FALSE; }else if (GUARD_IS_OK(ptr)) { return TRUE; } cl_log(LOG_ERR , "cl_is_allocated: supplied storage is guard-corrupted at 0x%lx" , (unsigned long)ptr); cl_dump_item(CBHDR(ptr)); DUMPIFASKED(); return FALSE; #else return (ptr != NULL); #endif } /* * cl_free: "free" clone */ void cl_free(void *ptr) { int bucket; struct cl_bucket* bhdr; if (!cl_malloc_inityet) { cl_malloc_init(); } if (ptr == NULL) { cl_log(LOG_ERR, "attempt to free NULL pointer in cl_free()"); DUMPIFASKED(); return; } /* Find the beginning of our "hidden" structure */ bhdr = BHDR(ptr); #ifdef HA_MALLOC_MAGIC switch (bhdr->hdr.magic) { case HA_MALLOC_MAGIC: break; case HA_FREE_MAGIC: cl_log(LOG_ERR , "cl_free: attempt to free already-freed" " object at 0x%lx" , (unsigned long)ptr); cl_dump_item(bhdr); DUMPIFASKED(); return; break; default: cl_log(LOG_ERR, "cl_free: Bad magic number" " in object at 0x%lx" , (unsigned long)ptr); cl_dump_item(bhdr); DUMPIFASKED(); return; break; } #endif if (!GUARD_IS_OK(ptr)) { cl_log(LOG_ERR , "cl_free: attempt to free guard-corrupted" " object at 0x%lx", (unsigned long)ptr); cl_dump_item(bhdr); DUMPIFASKED(); return; } #ifdef HA_MALLOC_TRACK cl_ptr_release(ptr); #endif bucket = bhdr->hdr.bucket; #ifdef HA_MALLOC_MAGIC bhdr->hdr.magic = HA_FREE_MAGIC; #endif /* * Return it to the appropriate bucket (linked list), or just free * it if it didn't come from one of our lists... */ #ifndef RETURN_TO_MALLOC if (bucket >= NUMBUCKS) { #endif #ifdef MARK_PRISTINE /* Is this size right? */ cl_mark_pristine(ptr, bhdr->hdr.reqsize); #endif if (memstats) { memstats->nbytes_req -= bhdr->hdr.reqsize; memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize); memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize); } free(bhdr); #ifndef RETURN_TO_MALLOC }else{ int bucksize = cl_bucket_sizes[bucket]; #if defined(USE_ASSERTS) g_assert(bhdr->hdr.reqsize <= cl_bucket_sizes[bucket]); # endif if (memstats) { memstats->nbytes_req -= bhdr->hdr.reqsize; memstats->nbytes_alloc -= MALLOCSIZE(bucksize); } bhdr->next = cl_malloc_buckets[bucket]; cl_malloc_buckets[bucket] = bhdr; #ifdef MARK_PRISTINE cl_mark_pristine(ptr, bucksize); # endif } #endif /* RETURN_TO_MALLOC */ if (memstats) { memstats->numfree++; } } void* cl_realloc(void *ptr, size_t newsize) { struct cl_bucket* bhdr; int bucket; size_t bucksize; if (!cl_malloc_inityet) { cl_malloc_init(); } if (memstats) { memstats->numrealloc++; } if (ptr == NULL) { /* NULL is a legal 'ptr' value for realloc... */ return cl_malloc(newsize); } if (newsize == 0) { /* realloc() is the most redundant interface ever */ cl_free(ptr); return NULL; } /* Find the beginning of our "hidden" structure */ bhdr = BHDR(ptr); #ifdef HA_MALLOC_MAGIC switch (bhdr->hdr.magic) { case HA_MALLOC_MAGIC: break; case HA_FREE_MAGIC: cl_log(LOG_ERR , "cl_realloc: attempt to realloc already-freed" " object at 0x%lx" , (unsigned long)ptr); cl_dump_item(bhdr); DUMPIFASKED(); return NULL; break; default: cl_log(LOG_ERR, "cl_realloc: Bad magic number" " in object at 0x%lx" , (unsigned long)ptr); cl_dump_item(bhdr); DUMPIFASKED(); return NULL; break; } #endif CHECK_GUARD_BYTES(ptr, "cl_realloc"); bucket = bhdr->hdr.bucket; /* * Figure out which bucket it came from... If any... */ if (bucket >= NUMBUCKS) { /* Not from our bucket-area... Call realloc... */ if (memstats) { memstats->nbytes_req -= bhdr->hdr.reqsize; memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize); memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize); memstats->nbytes_req += newsize; memstats->nbytes_alloc += MALLOCSIZE(newsize); memstats->mallocbytes += MALLOCSIZE(newsize); } #ifdef HA_MALLOC_TRACK cl_ptr_release(ptr); #endif bhdr = realloc(bhdr, newsize + cl_malloc_hdr_offset + GUARDSIZE); if (!bhdr) { return NULL; } #ifdef HA_MALLOC_TRACK cl_ptr_track(ptr); cl_ptr_tag(ptr, "cl_malloc.c", "realloc", 0); #endif bhdr->hdr.reqsize = newsize; ptr = (((char*)bhdr)+cl_malloc_hdr_offset); ADD_GUARD(ptr); CHECK_GUARD_BYTES(ptr, "cl_realloc - real realloc return value"); /* Not really a memory leak... BEAM thinks so though... */ return ptr; /*memory leak*/ } bucksize = cl_bucket_sizes[bucket]; #if defined(USE_ASSERTS) g_assert(bhdr->hdr.reqsize <= bucksize); #endif if (newsize > bucksize) { /* Need to allocate new space for it */ void* newret = cl_malloc(newsize); if (newret != NULL) { memcpy(newret, ptr, bhdr->hdr.reqsize); CHECK_GUARD_BYTES(newret, "cl_realloc - cl_malloc case"); } cl_free(ptr); return newret; } /* Amazing! It fits into the space previously allocated for it! */ bhdr->hdr.reqsize = newsize; if (memstats) { memstats->nbytes_req -= bhdr->hdr.reqsize; memstats->nbytes_req += newsize; } ADD_GUARD(ptr); CHECK_GUARD_BYTES(ptr, "cl_realloc - fits in existing space"); return ptr; } /* * cl_new_mem: use the real malloc to allocate some new memory */ static void* cl_new_mem(size_t size, int numbuck) { struct cl_bucket* hdrret; size_t allocsize; size_t mallocsize; if (numbuck < NUMBUCKS) { allocsize = cl_bucket_sizes[numbuck]; }else{ allocsize = size; } mallocsize = MALLOCSIZE(allocsize); if (numbuck == NOBUCKET) { mallocsize = (((mallocsize + (MALLOCROUND-1))/MALLOCROUND)*MALLOCROUND); } if ((hdrret = malloc(mallocsize)) == NULL) { return NULL; } hdrret->hdr.reqsize = size; hdrret->hdr.bucket = numbuck; #ifdef HA_MALLOC_MAGIC hdrret->hdr.magic = HA_MALLOC_MAGIC; #endif #ifdef HA_MALLOC_TRACK hdrret->hdr.left = NULL; hdrret->hdr.right = NULL; hdrret->hdr.owner[0] = '\0'; hdrret->hdr.dumped = 0; #endif if (memstats) { memstats->nbytes_alloc += mallocsize; memstats->nbytes_req += size; memstats->mallocbytes += mallocsize; } /* BEAM BUG -- this is NOT a leak */ return(((char*)hdrret)+cl_malloc_hdr_offset); /*memory leak*/ } /* * cl_calloc: calloc clone */ void * cl_calloc(size_t nmemb, size_t size) { void * ret = cl_malloc(nmemb*size); if (ret != NULL) { memset(ret, 0, nmemb*size); #ifdef HA_MALLOC_TRACK cl_ptr_tag(ret, "cl_malloc.c", "cl_calloc", 0); #endif } return(ret); } #ifdef HA_MALLOC_TRACK void * cl_calloc_track(size_t nmemb, size_t size, const char *file, const char *function, const int line) { void* ret; ret = cl_calloc(nmemb, size); if (ret) { cl_ptr_tag(ret, file, function, line); } return ret; } void* cl_realloc_track(void *ptr, size_t newsize, const char *file, const char *function, const int line) { void* ret; ret = cl_realloc(ptr, newsize); if (ret) { cl_ptr_tag(ret, file, function, line); } return ret; } void * cl_malloc_track(size_t size, const char *file, const char *function, const int line) { void* ret; ret = cl_malloc(size); if (ret) { /* Retag with the proper owner. */ cl_ptr_tag(ret, file, function, line); } return ret; } #endif /* * cl_strdup: strdup clone */ char * cl_strdup(const char *s) { void * ret; if (!s) { cl_log(LOG_ERR, "cl_strdup(NULL)"); return(NULL); } ret = cl_malloc((strlen(s) + 1) * sizeof(char)); if (ret) { strcpy(ret, s); } return(ret); } /* * cl_malloc_init(): initialize our malloc wrapper things */ static void cl_malloc_init() { int j; size_t cursize = 32; int llcount = 1; cl_malloc_inityet = 1; /* cl_malloc_hdr_offset should be a double-word multiple */ while (cl_malloc_hdr_offset > (llcount * sizeof(long long))) { llcount++; } cl_malloc_hdr_offset = llcount * sizeof(long long); for (j=0; j < NUMBUCKS; ++j) { cl_malloc_buckets[j] = NULL; cl_bucket_sizes[j] = cursize; cursize <<= 1; } buckminpow2 = INT2POW2(cl_bucket_sizes[0]-1); #ifdef MARK_PRISTINE { struct cl_bucket b; pristoff = (unsigned char*)&(b.next)-(unsigned char*)&b; pristoff += sizeof(b.next); } #endif #ifdef HA_MALLOC_TRACK cl_ptr_init(); #endif } void cl_malloc_setstats(volatile cl_mem_stats_t *stats) { if (memstats && stats) { *stats = *memstats; } memstats = stats; } volatile cl_mem_stats_t * cl_malloc_getstats(void) { return memstats; } static void cl_dump_item(const struct cl_bucket*b) { const unsigned char * cbeg; const unsigned char * cend; const unsigned char * cp; cl_log(LOG_INFO, "Dumping cl_malloc item @ 0x%lx, bucket address: 0x%lx" , ((unsigned long)b)+cl_malloc_hdr_offset, (unsigned long)b); #ifdef HA_MALLOC_TRACK cl_log(LOG_INFO, "Owner: %s" , b->hdr.owner); #endif #ifdef HA_MALLOC_MAGIC cl_log(LOG_INFO, "Magic number: 0x%lx reqsize=%ld" ", bucket=%d, bucksize=%ld" , b->hdr.magic , (long)b->hdr.reqsize, b->hdr.bucket , (long)(b->hdr.bucket >= NUMBUCKS ? 0 : cl_bucket_sizes[b->hdr.bucket])); #else cl_log(LOG_INFO, "reqsize=%ld" ", bucket=%d, bucksize=%ld" , (long)b->hdr.reqsize, b->hdr.bucket , (long)(b->hdr.bucket >= NUMBUCKS ? 0 : cl_bucket_sizes[b->hdr.bucket])); #endif cbeg = ((const unsigned char *)b)+cl_malloc_hdr_offset; cend = cbeg+b->hdr.reqsize+GUARDSIZE; for (cp=cbeg; cp < cend; cp+= sizeof(unsigned)) { cl_log(LOG_INFO, "%02x %02x %02x %02x \"%c%c%c%c\"" , (unsigned)cp[0], (unsigned)cp[1] , (unsigned)cp[2], (unsigned)cp[3] , cp[0], cp[1], cp[2], cp[3]); } } /* The only reason these functions exist is because glib uses non-standard * types (gsize)in place of size_t. Since size_t is 64-bits on some * machines where gsize (unsigned int) is 32-bits, this is annoying. */ static gpointer cl_malloc_glib(gsize n_bytes) { return (gpointer)cl_malloc((size_t)n_bytes); } static void cl_free_glib(gpointer mem) { cl_free((void*)mem); } static void * cl_realloc_glib(gpointer mem, gsize n_bytes) { return cl_realloc((void*)mem, (size_t)n_bytes); } /* Call before using any glib functions(!) */ /* See also: g_mem_set_vtable() */ void cl_malloc_forced_for_glib(void) { static GMemVTable vt = { cl_malloc_glib, cl_realloc_glib, cl_free_glib, NULL, NULL, NULL, }; if (!cl_malloc_inityet) { cl_malloc_init(); } g_mem_set_vtable(&vt); } #ifdef MARK_PRISTINE static int cl_check_is_pristine(const void* v, unsigned size) { const unsigned char * cp; const unsigned char * last; cp = v; last = cp + size; cp += pristoff; for (;cp < last; ++cp) { if (*cp != PRISTVALUE) { return FALSE; } } return TRUE; } static void cl_mark_pristine(void* v, unsigned size) { unsigned char * cp = v; memset(cp+pristoff, PRISTVALUE, size-pristoff); } #endif #endif /* _CLPLUMBING_CLMALLOC_NATIVE_H */ Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_misc.c0000644000000000000000000001012112120057602025733 0ustar00usergroup00000000000000/* * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_TIME_H #include #endif #include int cl_str_to_boolean(const char * s, int * ret) { if(s == NULL) { return HA_FAIL; } if ( strcasecmp(s, "true") == 0 || strcasecmp(s, "on") == 0 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0){ *ret = TRUE; return HA_OK; } if ( strcasecmp(s, "false") == 0 || strcasecmp(s, "off") == 0 || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0){ *ret = FALSE; return HA_OK; } return HA_FAIL; } int cl_file_exists(const char* filename) { struct stat st; if (filename == NULL){ cl_log(LOG_ERR, "%s: NULL filename", __FUNCTION__); return FALSE; } if (lstat(filename, &st) == 0){ return S_ISREG(st.st_mode); } return FALSE; } char* cl_get_env(const char* env_name) { if (env_name == NULL){ cl_log(LOG_ERR, "%s: null name", __FUNCTION__); return NULL; } return getenv(env_name); } int cl_binary_to_int(const char* data, int len) { const char *p = data; const char *pmax = p + len; guint h = *p; if (h){ for (p += 1; p < pmax; p++){ h = (h << 5) - h + *p; } } return h; } /* * Convert a string into a positive, rounded number of milliseconds. * * Returns -1 on error. * * Permissible forms: * [0-9]+ units are seconds * [0-9]*.[0-9]+ units are seconds * [0-9]+ *[Mm][Ss] units are milliseconds * [0-9]*.[0-9]+ *[Mm][Ss] units are milliseconds * [0-9]+ *[Uu][Ss] units are microseconds * [0-9]*.[0-9]+ *[Uu][Ss] units are microseconds * * Examples: * * 1 = 1000 milliseconds * 1000ms = 1000 milliseconds * 1000000us = 1000 milliseconds * 0.1 = 100 milliseconds * 100ms = 100 milliseconds * 100000us = 100 milliseconds * 0.001 = 1 millisecond * 1ms = 1 millisecond * 1000us = 1 millisecond * 499us = 0 milliseconds * 501us = 1 millisecond */ #define NUMCHARS "0123456789." #define WHITESPACE " \t\n\r\f" #define EOS '\0' long cl_get_msec(const char * input) { const char * cp = input; const char * units; long multiplier = 1000; long divisor = 1; long ret = -1; double dret; cp += strspn(cp, WHITESPACE); units = cp + strspn(cp, NUMCHARS); units += strspn(units, WHITESPACE); if (strchr(NUMCHARS, *cp) == NULL) { return ret; } if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "cl_get_msec", 4) == 0) { multiplier = 1; divisor = 1; }else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) { multiplier = 1; divisor = 1000; }else if (*units != EOS && *units != '\n' && *units != '\r') { return ret; } dret = atof(cp); dret *= (double)multiplier; dret /= (double)divisor; dret += 0.5; ret = (long)dret; return(ret); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_msg.c0000644000000000000000000014560512120057602025606 0ustar00usergroup00000000000000/* * Heartbeat messaging object. * * Copyright (C) 2000 Alan Robertson * * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXMSGLINE 512 #define MINFIELDS 30 #define NEWLINE "\n" #define NEEDAUTH 1 #define NOAUTH 0 #define MAX_INT_LEN 64 #define MAX_NAME_LEN 255 #define UUID_SLEN 64 #define MAXCHILDMSGLEN 512 static int compression_threshold = (128*1024); static enum cl_msgfmt msgfmt = MSGFMT_NVPAIR; static gboolean use_traditional_compression = FALSE; const char* FT_strings[]={ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; #undef DOAUDITS #define DOAUDITS #undef DOPARANOIDAUDITS /* #define DOPARANOIDAUDITS */ #ifdef DOAUDITS void ha_msg_audit(const struct ha_msg* msg); # define AUDITMSG(msg) ha_msg_audit(msg) # ifdef DOPARANOIDAUDITS # define PARANOIDAUDITMSG(msg) ha_msg_audit(msg) # else # define PARANOIDAUDITMSG(msg) /*nothing*/ # endif #else # define AUDITMSG(msg) /*nothing*/ # define PARANOIDAUDITMSG(msg) /*nothing*/ #endif static volatile hb_msg_stats_t* msgstats = NULL; gboolean cl_msg_quiet_fmterr = FALSE; extern int netstring_format; static struct ha_msg* wirefmt2msg_ll(const char* s, size_t length, int need_auth); struct ha_msg* string2msg_ll(const char * s, size_t length, int need_auth, int depth); extern int struct_stringlen(size_t namlen, size_t vallen, const void* value); extern int struct_netstringlen(size_t namlen, size_t vallen, const void* value); extern int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen); static char* msg2wirefmt_ll(struct ha_msg*m, size_t* len, gboolean need_compress); extern GHashTable* CompressFuncs; void cl_set_traditional_compression(gboolean value) { use_traditional_compression = value; if (use_traditional_compression && CompressFuncs) { cl_log(LOG_WARNING , "Traditional compression selected" ". Realtime behavior will likely be impacted(!)"); cl_log(LOG_INFO , "See %s for more information." , HAURL("Ha.cf#traditional_compression_-_controls_compression_mode")); } } void cl_set_compression_threshold(size_t threadhold) { compression_threshold = threadhold; } void cl_msg_setstats(volatile hb_msg_stats_t* stats) { msgstats = stats; } static int msg_stats_fd = -1; static int cl_msg_stats_open(const char* filename) { if (filename == NULL){ cl_log(LOG_ERR, "%s: filename is NULL", __FUNCTION__); return -1; } return open(filename, O_WRONLY|O_CREAT|O_APPEND, 0644); } static int cl_msg_stats_close(void) { if (msg_stats_fd > 0){ close(msg_stats_fd); } msg_stats_fd = -1; return HA_OK; } #define STATSFILE "/var/log/ha_msg_stats" int cl_msg_stats_add(longclock_t time, int size) { char buf[MAXLINE]; int len; if (msg_stats_fd < 0){ msg_stats_fd = cl_msg_stats_open(STATSFILE); if (msg_stats_fd < 0){ cl_log(LOG_ERR, "%s:opening file failed", __FUNCTION__); return HA_FAIL; } } sprintf(buf, "%lld %d\n", (long long)time, size); len = strnlen(buf, MAXLINE); if (write(msg_stats_fd, buf, len) == len){ cl_msg_stats_close(); return HA_OK; } cl_msg_stats_close(); return HA_FAIL;; } /* Set default messaging format */ void cl_set_msg_format(enum cl_msgfmt mfmt) { msgfmt = mfmt; } void cl_dump_msgstats(void) { if (msgstats){ cl_log(LOG_INFO, "dumping msg stats: " "allocmsgs=%lu", msgstats->allocmsgs); } return; } void list_cleanup(GList* list) { size_t i; for (i = 0; i < g_list_length(list); i++){ char* element = g_list_nth_data(list, i); if (element == NULL){ cl_log(LOG_WARNING, "list_cleanup:" "element is NULL"); continue; } free(element); } g_list_free(list); } /* Create a new (empty) message */ struct ha_msg * ha_msg_new(int nfields) { struct ha_msg * ret; int nalloc; ret = MALLOCT(struct ha_msg); if (ret) { ret->nfields = 0; if (nfields > MINFIELDS) { nalloc = nfields; } else { nalloc = MINFIELDS; } ret->nalloc = nalloc; ret->names = (char **)calloc(sizeof(char *), nalloc); ret->nlens = (size_t *)calloc(sizeof(size_t), nalloc); ret->values = (void **)calloc(sizeof(void *), nalloc); ret->vlens = (size_t *)calloc(sizeof(size_t), nalloc); ret->types = (int*)calloc(sizeof(int), nalloc); if (ret->names == NULL || ret->values == NULL || ret->nlens == NULL || ret->vlens == NULL || ret->types == NULL) { cl_log(LOG_ERR, "%s" , "ha_msg_new: out of memory for ha_msg"); /* It is safe to give this to ha_msg_del() */ /* at this point. It's well-enough-formed */ ha_msg_del(ret); /*violated property*/ ret = NULL; }else if (msgstats) { msgstats->allocmsgs++; msgstats->totalmsgs++; msgstats->lastmsg = time_longclock(); } } return(ret); } /* Delete (destroy) a message */ void ha_msg_del(struct ha_msg *msg) { if (msg) { int j; PARANOIDAUDITMSG(msg); if (msgstats) { msgstats->allocmsgs--; } if (msg->names) { for (j=0; j < msg->nfields; ++j) { if (msg->names[j]) { free(msg->names[j]); msg->names[j] = NULL; } } free(msg->names); msg->names = NULL; } if (msg->values) { for (j=0; j < msg->nfields; ++j) { if (msg->values[j] == NULL){ continue; } if(msg->types[j] < DIMOF(fieldtypefuncs)){ fieldtypefuncs[msg->types[j]].memfree(msg->values[j]); } } free(msg->values); msg->values = NULL; } if (msg->nlens) { free(msg->nlens); msg->nlens = NULL; } if (msg->vlens) { free(msg->vlens); msg->vlens = NULL; } if (msg->types){ free(msg->types); msg->types = NULL; } msg->nfields = -1; msg->nalloc = -1; free(msg); } } struct ha_msg* ha_msg_copy(const struct ha_msg *msg) { struct ha_msg* ret; int j; PARANOIDAUDITMSG(msg); if (msg == NULL || (ret = ha_msg_new(msg->nalloc)) == NULL) { return NULL; } ret->nfields = msg->nfields; memcpy(ret->nlens, msg->nlens, sizeof(msg->nlens[0])*msg->nfields); memcpy(ret->vlens, msg->vlens, sizeof(msg->vlens[0])*msg->nfields); memcpy(ret->types, msg->types, sizeof(msg->types[0])*msg->nfields); for (j=0; j < msg->nfields; ++j) { if ((ret->names[j] = malloc(msg->nlens[j]+1)) == NULL) { goto freeandleave; } memcpy(ret->names[j], msg->names[j], msg->nlens[j]+1); if(msg->types[j] < DIMOF(fieldtypefuncs)){ ret->values[j] = fieldtypefuncs[msg->types[j]].dup(msg->values[j], msg->vlens[j]); if (!ret->values[j]){ cl_log(LOG_ERR,"duplicating the message field failed"); goto freeandleave; } } } return ret; freeandleave: /* * ha_msg_del nicely handles partially constructed ha_msgs * so, there's not really a memory leak here at all, but BEAM * thinks there is. */ ha_msg_del(ret);/* memory leak */ ret=NULL; return ret; } #ifdef DOAUDITS void ha_msg_audit(const struct ha_msg* msg) { int doabort = FALSE; int j; if (!msg) { return; } if (!msg) { cl_log(LOG_CRIT, "Message @ %p is not allocated" , msg); abort(); } if (msg->nfields < 0) { cl_log(LOG_CRIT, "Message @ %p has negative fields (%d)" , msg, msg->nfields); doabort = TRUE; } if (msg->nalloc < 0) { cl_log(LOG_CRIT, "Message @ %p has negative nalloc (%d)" , msg, msg->nalloc); doabort = TRUE; } if (!msg->names) { cl_log(LOG_CRIT , "Message names @ %p is not allocated" , msg->names); doabort = TRUE; } if (!msg->values) { cl_log(LOG_CRIT , "Message values @ %p is not allocated" , msg->values); doabort = TRUE; } if (!msg->nlens) { cl_log(LOG_CRIT , "Message nlens @ %p is not allocated" , msg->nlens); doabort = TRUE; } if (!msg->vlens) { cl_log(LOG_CRIT , "Message vlens @ %p is not allocated" , msg->vlens); doabort = TRUE; } if (doabort) { cl_log_message(LOG_INFO,msg); abort(); } for (j=0; j < msg->nfields; ++j) { if (msg->nlens[j] == 0){ cl_log(LOG_ERR, "zero namelen found in msg"); abort(); } if (msg->types[j] == FT_STRING){ if (msg->vlens[j] != strlen(msg->values[j])){ cl_log(LOG_ERR, "stringlen does not match"); cl_log_message(LOG_INFO,msg); abort(); } } if (!msg->names[j]) { cl_log(LOG_CRIT, "Message name[%d] @ 0x%p" " is not allocated." , j, msg->names[j]); abort(); } if (msg->types[j] != FT_LIST && !msg->values[j]) { cl_log(LOG_CRIT, "Message value [%d] @ 0x%p" " is not allocated.", j, msg->values[j]); cl_log_message(LOG_INFO, msg); abort(); } } } #endif int ha_msg_expand(struct ha_msg* msg ) { char ** names ; size_t *nlens ; void ** values ; size_t* vlens ; int * types ; int nalloc; if(!msg){ cl_log(LOG_ERR, "ha_msg_expand:" "input msg is null"); return HA_FAIL; } names = msg->names; nlens = msg->nlens; values = msg->values; vlens = msg->vlens; types = msg->types; nalloc = msg->nalloc + MINFIELDS; msg->names = (char **)calloc(sizeof(char *), nalloc); msg->nlens = (size_t *)calloc(sizeof(size_t), nalloc); msg->values = (void **)calloc(sizeof(void *), nalloc); msg->vlens = (size_t *)calloc(sizeof(size_t), nalloc); msg->types= (int*)calloc(sizeof(int), nalloc); if (msg->names == NULL || msg->values == NULL || msg->nlens == NULL || msg->vlens == NULL || msg->types == NULL) { cl_log(LOG_ERR, "%s" , " out of memory for ha_msg"); return(HA_FAIL); } memcpy(msg->names, names, msg->nalloc*sizeof(char *)); memcpy(msg->nlens, nlens, msg->nalloc*sizeof(size_t)); memcpy(msg->values, values, msg->nalloc*sizeof(void *)); memcpy(msg->vlens, vlens, msg->nalloc*sizeof(size_t)); memcpy(msg->types, types, msg->nalloc*sizeof(int)); free(names); free(nlens); free(values); free(vlens); free(types); msg->nalloc = nalloc; return HA_OK; } int cl_msg_remove_value(struct ha_msg* msg, const void* value) { int j; if (msg == NULL || value == NULL){ cl_log(LOG_ERR, "cl_msg_remove: invalid argument"); return HA_FAIL; } for (j = 0; j < msg->nfields; ++j){ if (value == msg->values[j]){ break; } } if (j == msg->nfields){ cl_log(LOG_ERR, "cl_msg_remove: field %p not found", value); return HA_FAIL; } return cl_msg_remove_offset(msg, j); } int cl_msg_remove(struct ha_msg* msg, const char* name) { int j; if (msg == NULL || name == NULL){ cl_log(LOG_ERR, "cl_msg_remove: invalid argument"); return HA_FAIL; } for (j = 0; j < msg->nfields; ++j){ if (strcmp(name, msg->names[j]) == 0){ break; } } if (j == msg->nfields){ cl_log(LOG_ERR, "cl_msg_remove: field %s not found", name); return HA_FAIL; } return cl_msg_remove_offset(msg, j); } int cl_msg_remove_offset(struct ha_msg* msg, int offset) { int j = offset; int i; if (j == msg->nfields){ cl_log(LOG_ERR, "cl_msg_remove: field %d not found", j); return HA_FAIL; } free(msg->names[j]); fieldtypefuncs[msg->types[j]].memfree(msg->values[j]); for (i= j + 1; i < msg->nfields ; i++){ msg->names[i -1] = msg->names[i]; msg->nlens[i -1] = msg->nlens[i]; msg->values[i -1] = msg->values[i]; msg->vlens[i-1] = msg->vlens[i]; msg->types[i-1] = msg->types[i]; } msg->nfields--; return HA_OK; } /* low level implementation for ha_msg_add the caller is responsible to allocate/free memories for @name and @value. */ static int ha_msg_addraw_ll(struct ha_msg * msg, char * name, size_t namelen, void * value, size_t vallen, int type, int depth) { size_t startlen = sizeof(MSG_START)-1; int (*addfield) (struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth); if (!msg || msg->names == NULL || (msg->values == NULL) ) { cl_log(LOG_ERR, "ha_msg_addraw_ll: cannot add field to ha_msg"); return(HA_FAIL); } if (msg->nfields >= msg->nalloc) { if( ha_msg_expand(msg) != HA_OK){ cl_log(LOG_ERR, "message expanding failed"); return(HA_FAIL); } } if (namelen >= startlen && name[0] == '>' && strncmp(name, MSG_START, startlen) == 0) { if(!cl_msg_quiet_fmterr) { cl_log(LOG_ERR, "ha_msg_addraw_ll: illegal field"); } return(HA_FAIL); } if (name == NULL || (value == NULL) || namelen <= 0 || vallen < 0) { cl_log(LOG_ERR, "ha_msg_addraw_ll: " "cannot add name/value to ha_msg"); return(HA_FAIL); } HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs)); addfield = fieldtypefuncs[type].addfield; if (!addfield || addfield(msg, name, namelen, value, vallen,depth) != HA_OK){ cl_log(LOG_ERR, "ha_msg_addraw_ll: addfield failed"); return(HA_FAIL); } PARANOIDAUDITMSG(msg); return(HA_OK); } static int ha_msg_addraw(struct ha_msg * msg, const char * name, size_t namelen, const void * value, size_t vallen, int type, int depth) { char *cpvalue = NULL; char *cpname = NULL; int ret; if (namelen == 0){ cl_log(LOG_ERR, "%s: Adding a field with 0 name length", __FUNCTION__); return HA_FAIL; } if ((cpname = malloc(namelen+1)) == NULL) { cl_log(LOG_ERR, "ha_msg_addraw: no memory for string (name)"); return(HA_FAIL); } strncpy(cpname, name, namelen); cpname[namelen] = EOS; HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs)); if (fieldtypefuncs[type].dup){ cpvalue = fieldtypefuncs[type].dup(value, vallen); } if (cpvalue == NULL){ cl_log(LOG_ERR, "ha_msg_addraw: copying message failed"); free(cpname); return(HA_FAIL); } ret = ha_msg_addraw_ll(msg, cpname, namelen, cpvalue, vallen , type, depth); if (ret != HA_OK){ cl_log(LOG_ERR, "ha_msg_addraw(): ha_msg_addraw_ll failed"); free(cpname); fieldtypefuncs[type].memfree(cpvalue); } return(ret); } /*Add a null-terminated name and binary value to a message*/ int ha_msg_addbin(struct ha_msg * msg, const char * name, const void * value, size_t vallen) { return(ha_msg_addraw(msg, name, strlen(name), value, vallen, FT_BINARY, 0)); } int ha_msg_adduuid(struct ha_msg* msg, const char *name, const cl_uuid_t* u) { return(ha_msg_addraw(msg, name, strlen(name), u, sizeof(cl_uuid_t), FT_BINARY, 0)); } /*Add a null-terminated name and struct value to a message*/ int ha_msg_addstruct(struct ha_msg * msg, const char * name, const void * value) { const struct ha_msg* childmsg = (const struct ha_msg*) value; if (get_netstringlen(childmsg) > MAXCHILDMSGLEN || get_stringlen(childmsg) > MAXCHILDMSGLEN) { /*cl_log(LOG_WARNING, "%s: childmsg too big (name=%s, nslen=%d, len=%d)." " Use ha_msg_addstruct_compress() instead.", __FUNCTION__, name, get_netstringlen(childmsg), get_stringlen(childmsg)); */ } return ha_msg_addraw(msg, name, strlen(name), value, sizeof(struct ha_msg), FT_STRUCT, 0); } int ha_msg_addstruct_compress(struct ha_msg * msg, const char * name, const void * value) { if (use_traditional_compression){ return ha_msg_addraw(msg, name, strlen(name), value, sizeof(struct ha_msg), FT_STRUCT, 0); }else{ return ha_msg_addraw(msg, name, strlen(name), value, sizeof(struct ha_msg), FT_UNCOMPRESS, 0); } } int ha_msg_add_int(struct ha_msg * msg, const char * name, int value) { char buf[MAX_INT_LEN]; snprintf(buf, MAX_INT_LEN, "%d", value); return (ha_msg_add(msg, name, buf)); } int ha_msg_mod_int(struct ha_msg * msg, const char * name, int value) { char buf[MAX_INT_LEN]; snprintf(buf, MAX_INT_LEN, "%d", value); return (cl_msg_modstring(msg, name, buf)); } int ha_msg_value_int(const struct ha_msg * msg, const char * name, int* value) { const char* svalue = ha_msg_value(msg, name); if(NULL == svalue) { return HA_FAIL; } *value = atoi(svalue); return HA_OK; } int ha_msg_add_ul(struct ha_msg * msg, const char * name, unsigned long value) { char buf[MAX_INT_LEN]; snprintf(buf, MAX_INT_LEN, "%lu", value); return (ha_msg_add(msg, name, buf)); } int ha_msg_mod_ul(struct ha_msg * msg, const char * name, unsigned long value) { char buf[MAX_INT_LEN]; snprintf(buf, MAX_INT_LEN, "%lu", value); return (cl_msg_modstring(msg, name, buf)); } int ha_msg_value_ul(const struct ha_msg * msg, const char * name, unsigned long* value) { const char* svalue = ha_msg_value(msg, name); if(NULL == svalue) { return HA_FAIL; } *value = strtoul(svalue, NULL, 10); return HA_OK; } /* * ha_msg_value_str_list()/ha_msg_add_str_list(): * transform a string list suitable for putting into an ha_msg is by a convention * of naming the fields into the following format: * listname1=foo * listname2=bar * listname3=stuff * etc. */ GList* ha_msg_value_str_list(struct ha_msg * msg, const char * name) { int i = 1; int len = 0; const char* value; char* element; GList* list = NULL; if( NULL==msg||NULL==name||strnlen(name, MAX_NAME_LEN)>=MAX_NAME_LEN ){ return NULL; } len = cl_msg_list_length(msg,name); for(i=0; infields; i++) { if( FT_STRING != msg->types[i] ) { continue; } g_hash_table_insert(hash_table, g_strndup(msg->names[i],msg->nlens[i]), g_strndup(msg->values[i],msg->vlens[i])); } return hash_table; } GHashTable* ha_msg_value_str_table(struct ha_msg * msg, const char * name) { struct ha_msg* hash_msg; GHashTable * hash_table = NULL; if (NULL == msg || NULL == name) { return NULL; } hash_msg = cl_get_struct(msg, name); if (NULL == hash_msg) { return NULL; } hash_table = msg_to_str_table(hash_msg); return hash_table; } int ha_msg_add_str_table(struct ha_msg * msg, const char * name, GHashTable* hash_table) { struct ha_msg* hash_msg; if (NULL == msg || NULL == name || NULL == hash_table) { return HA_FAIL; } hash_msg = str_table_to_msg(hash_table); if( HA_OK != ha_msg_addstruct(msg, name, hash_msg)) { ha_msg_del(hash_msg); cl_log(LOG_ERR , "ha_msg_addstruct in ha_msg_add_str_table failed"); return HA_FAIL; } ha_msg_del(hash_msg); return HA_OK; } int ha_msg_mod_str_table(struct ha_msg * msg, const char * name, GHashTable* hash_table) { struct ha_msg* hash_msg; if (NULL == msg || NULL == name || NULL == hash_table) { return HA_FAIL; } hash_msg = str_table_to_msg(hash_table); if( HA_OK != cl_msg_modstruct(msg, name, hash_msg)) { ha_msg_del(hash_msg); cl_log(LOG_ERR , "ha_msg_modstruct in ha_msg_mod_str_table failed"); return HA_FAIL; } ha_msg_del(hash_msg); return HA_OK; } int cl_msg_list_add_string(struct ha_msg* msg, const char* name, const char* value) { GList* list = NULL; int ret; if(!msg || !name || !value){ cl_log(LOG_ERR, "cl_msg_list_add_string: input invalid"); return HA_FAIL; } list = g_list_append(list, UNCONST_CAST_POINTER(gpointer, value)); if (!list){ cl_log(LOG_ERR, "cl_msg_list_add_string: append element to" "a glist failed"); return HA_FAIL; } ret = ha_msg_addraw(msg, name, strlen(name), list, string_list_pack_length(list), FT_LIST, 0); g_list_free(list); return ret; } /* Add a null-terminated name and value to a message */ int ha_msg_add(struct ha_msg * msg, const char * name, const char * value) { if(name == NULL || value == NULL) { return HA_FAIL; } return(ha_msg_nadd(msg, name, strlen(name), value, strlen(value))); } /* Add a name/value pair to a message (with sizes for name and value) */ int ha_msg_nadd(struct ha_msg * msg, const char * name, int namelen , const char * value, int vallen) { return(ha_msg_addraw(msg, name, namelen, value, vallen, FT_STRING, 0)); } /* Add a name/value/type to a message (with sizes for name and value) */ int ha_msg_nadd_type(struct ha_msg * msg, const char * name, int namelen , const char * value, int vallen, int type) { return(ha_msg_addraw(msg, name, namelen, value, vallen, type, 0)); } /* Add a "name=value" line to the name, value pairs in a message */ static int ha_msg_add_nv_depth(struct ha_msg* msg, const char * nvline, const char * bufmax, int depth) { int namelen; const char * valp; int vallen; if (!nvline) { cl_log(LOG_ERR, "ha_msg_add_nv: NULL nvline"); return(HA_FAIL); } /* How many characters before the '='? */ if ((namelen = strcspn(nvline, EQUAL)) <= 0 || nvline[namelen] != '=') { if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING , "ha_msg_add_nv_depth: line doesn't contain '='"); cl_log(LOG_INFO, "%s", nvline); } return(HA_FAIL); } valp = nvline + namelen +1; /* Point just *past* the '=' */ if (valp >= bufmax){ return HA_FAIL; } vallen = strcspn(valp, NEWLINE); if ((valp + vallen) >= bufmax){ return HA_FAIL; } if (vallen == 0){ valp = NULL; } /* Call ha_msg_nadd to actually add the name/value pair */ return(ha_msg_addraw(msg, nvline, namelen, valp, vallen , FT_STRING, depth)); } int ha_msg_add_nv(struct ha_msg* msg, const char * nvline, const char * bufmax) { return(ha_msg_add_nv_depth(msg, nvline, bufmax, 0)); } static void * cl_get_value(const struct ha_msg * msg, const char * name, size_t * vallen, int *type) { int j; if (!msg || !msg->names || !msg->values) { cl_log(LOG_ERR, "%s: wrong argument (%s)", __FUNCTION__, name); return(NULL); } PARANOIDAUDITMSG(msg); for (j=0; j < msg->nfields; ++j) { const char *local_name = msg->names[j]; if (name[0] == local_name[0] && strcmp(name, local_name) == 0) { if (vallen){ *vallen = msg->vlens[j]; } if (type){ *type = msg->types[j]; } return(msg->values[j]); } } return(NULL); } static void * cl_get_value_mutate(struct ha_msg * msg, const char * name, size_t * vallen, int *type) { int j; if (!msg || !msg->names || !msg->values) { cl_log(LOG_ERR, "%s: wrong argument", __FUNCTION__); return(NULL); } AUDITMSG(msg); for (j=0; j < msg->nfields; ++j) { if (strcmp(name, msg->names[j]) == 0) { int tp = msg->types[j]; if (fieldtypefuncs[tp].pregetaction){ fieldtypefuncs[tp].pregetaction(msg, j); } if (vallen){ *vallen = msg->vlens[j]; } if (type){ *type = msg->types[j]; } return(msg->values[j]); } } return(NULL); } const void * cl_get_binary(const struct ha_msg *msg, const char * name, size_t * vallen) { const void *ret; int type; ret = cl_get_value( msg, name, vallen, &type); if (ret == NULL){ /* cl_log(LOG_WARNING, "field %s not found", name); cl_log_message(msg); */ return(NULL); } if ( type != FT_BINARY){ cl_log(LOG_WARNING, "field %s is not binary", name); cl_log_message(LOG_WARNING, msg); return(NULL); } return(ret); } /* UUIDs are stored with a machine-independent byte ordering (even though it's binary) */ int cl_get_uuid(const struct ha_msg *msg, const char * name, cl_uuid_t* retval) { const void * vret; size_t vretsize; cl_uuid_clear(retval); if ((vret = cl_get_binary(msg, name, &vretsize)/*discouraged function*/) == NULL) { /* But perfectly portable in this case */ return HA_FAIL; } if (vretsize != sizeof(cl_uuid_t)) { cl_log(LOG_WARNING, "Binary field %s is not a uuid.", name); cl_log(LOG_INFO, "expecting %d bytes, got %d bytes", (int)sizeof(cl_uuid_t), (int)vretsize); cl_log_message(LOG_INFO, msg); return HA_FAIL; } memcpy(retval, vret, sizeof(cl_uuid_t)); return HA_OK; } const char * cl_get_string(const struct ha_msg *msg, const char *name) { const void *ret; int type; ret = cl_get_value( msg, name, NULL, &type); if (ret == NULL || type != FT_STRING){ return(NULL); } return(ret); } int cl_get_type(const struct ha_msg *msg, const char *name) { const void *ret; int type; ret = cl_get_value( msg, name, NULL, &type); if (ret == NULL) { return -1; } if (type < 0){ cl_log(LOG_WARNING, "field %s not a valid type" , name); return(-1); } return(type); } /* struct ha_msg * cl_get_struct(const struct ha_msg *msg, const char* name) { struct ha_msg* ret; int type; size_t vallen; ret = cl_get_value(msg, name, &vallen, &type); if (ret == NULL ){ return(NULL); } switch(type){ case FT_STRUCT: break; default: cl_log(LOG_ERR, "%s: field %s is not a struct (%d)", __FUNCTION__, name, type); return NULL; } return ret; } */ struct ha_msg * cl_get_struct(struct ha_msg *msg, const char* name) { struct ha_msg* ret; int type = -1; size_t vallen; ret = cl_get_value_mutate(msg, name, &vallen, &type); if (ret == NULL ){ return(NULL); } switch(type){ case FT_UNCOMPRESS: case FT_STRUCT: break; default: cl_log(LOG_ERR, "%s: field %s is not a struct (%d)", __FUNCTION__, name, type); return NULL; } return ret; } int cl_msg_list_length(struct ha_msg* msg, const char* name) { GList* ret; int type; ret = cl_get_value( msg, name, NULL, &type); if ( ret == NULL || type != FT_LIST){ return -1; } return g_list_length(ret); } void* cl_msg_list_nth_data(struct ha_msg* msg, const char* name, int n) { GList* ret; int type; ret = cl_get_value( msg, name, NULL, &type); if ( ret == NULL || type != FT_LIST){ cl_log(LOG_WARNING, "field %s not found " " or type mismatch", name); return NULL; } return g_list_nth_data(ret, n); } int cl_msg_add_list(struct ha_msg* msg, const char* name, GList* list) { int ret; if(msg == NULL|| name ==NULL || list == NULL){ cl_log(LOG_ERR, "cl_msg_add_list:" "invalid arguments"); return HA_FAIL; } ret = ha_msg_addraw(msg, name, strlen(name), list, string_list_pack_length(list), FT_LIST, 0); return ret; } GList* cl_msg_get_list(struct ha_msg* msg, const char* name) { GList* ret; int type; ret = cl_get_value( msg, name, NULL, &type); if ( ret == NULL || type != FT_LIST){ cl_log(LOG_WARNING, "field %s not found " " or type mismatch", name); return NULL; } return ret; } int cl_msg_add_list_str(struct ha_msg* msg, const char* name, char** buf, size_t n) { GList* list = NULL; int i; int ret = HA_FAIL; if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){ cl_log(LOG_ERR, "%s:" "invalid parameter(%s)", !n <= 0?"n is negative or zero": !buf?"buf is NULL": !name?"name is NULL": "msg is NULL",__FUNCTION__); return HA_FAIL; } for ( i = 0; i < n; i++){ if (buf[i] == NULL){ cl_log(LOG_ERR, "%s: %dth element in buf is null", __FUNCTION__, i); goto free_and_out; } list = g_list_append(list, buf[i]); if (list == NULL){ cl_log(LOG_ERR, "%s:adding integer to list failed", __FUNCTION__); goto free_and_out; } } ret = ha_msg_addraw(msg, name, strlen(name), list, string_list_pack_length(list), FT_LIST, 0); free_and_out: if (list){ g_list_free(list); list = NULL; } return ret; } static void list_element_free(gpointer data, gpointer userdata) { if (data){ g_free(data); } } int cl_msg_add_list_int(struct ha_msg* msg, const char* name, int* buf, size_t n) { GList* list = NULL; size_t i; int ret = HA_FAIL; if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){ cl_log(LOG_ERR, "cl_msg_add_list_int:" "invalid parameter(%s)", !n <= 0?"n is negative or zero": !buf?"buf is NULL": !name?"name is NULL": "msg is NULL"); goto free_and_out; } for ( i = 0; i < n; i++){ char intstr[MAX_INT_LEN]; sprintf(intstr,"%d", buf[i]); list = g_list_append(list, g_strdup(intstr)); if (list == NULL){ cl_log(LOG_ERR, "cl_msg_add_list_int:" "adding integer to list failed"); goto free_and_out; } } ret = ha_msg_addraw(msg, name, strlen(name), list, string_list_pack_length(list), FT_LIST, 0); free_and_out: if (list){ g_list_foreach(list,list_element_free , NULL); g_list_free(list); list = NULL; } return ret; } int cl_msg_get_list_int(struct ha_msg* msg, const char* name, int* buf, size_t* n) { GList* list; size_t len; int i; GList* list_element; if (n == NULL || buf == NULL|| name ==NULL ||msg == NULL){ cl_log(LOG_ERR, "cl_msg_get_list_int:" "invalid parameter(%s)", !n?"n is NULL": !buf?"buf is NULL": !name?"name is NULL": "msg is NULL"); return HA_FAIL; } list = cl_msg_get_list(msg, name); if (list == NULL){ cl_log(LOG_ERR, "cl_msg_get_list_int:" "list of integers %s not found", name); return HA_FAIL; } len = g_list_length(list); if (len > *n){ cl_log(LOG_ERR, "cl_msg_get_list_int:" "buffer too small: *n=%ld, required len=%ld", (long)*n, (long)len); *n = len; return HA_FAIL; } *n = len; i = 0; list_element = g_list_first(list); while( list_element != NULL){ char* intstr = list_element->data; if (intstr == NULL){ cl_log(LOG_ERR, "cl_msg_get_list_int:" "element data is NULL"); return HA_FAIL; } if (sscanf(intstr,"%d", &buf[i]) != 1){ cl_log(LOG_ERR, "cl_msg_get_list_int:" "element data is NULL"); return HA_FAIL; } i++; list_element = g_list_next(list_element); } return HA_OK; } int cl_msg_replace_value(struct ha_msg* msg, const void *old_value, const void* value, size_t vlen, int type) { int j; if (msg == NULL || old_value == NULL) { cl_log(LOG_ERR, "cl_msg_replace: invalid argument"); return HA_FAIL; } for (j = 0; j < msg->nfields; ++j){ if (old_value == msg->values[j]){ break; } } if (j == msg->nfields){ cl_log(LOG_ERR, "cl_msg_replace: field %p not found", old_value); return HA_FAIL; } return cl_msg_replace(msg, j, value, vlen, type); } /*this function is for internal use only*/ int cl_msg_replace(struct ha_msg* msg, int index, const void* value, size_t vlen, int type) { void * newv ; int newlen = vlen; int oldtype; PARANOIDAUDITMSG(msg); if (msg == NULL || value == NULL) { cl_log(LOG_ERR, "%s: NULL input.", __FUNCTION__); return HA_FAIL; } if(type >= DIMOF(fieldtypefuncs)){ cl_log(LOG_ERR, "%s:" "invalid type(%d)",__FUNCTION__, type); return HA_FAIL; } if (index >= msg->nfields){ cl_log(LOG_ERR, "%s: index(%d) out of range(%d)", __FUNCTION__,index, msg->nfields); return HA_FAIL; } oldtype = msg->types[index]; newv = fieldtypefuncs[type].dup(value,vlen); if (!newv){ cl_log(LOG_ERR, "%s: duplicating message fields failed" "value=%p, vlen=%d, msg->names[i]=%s", __FUNCTION__,value, (int)vlen, msg->names[index]); return HA_FAIL; } fieldtypefuncs[oldtype].memfree(msg->values[index]); msg->values[index] = newv; msg->vlens[index] = newlen; msg->types[index] = type; PARANOIDAUDITMSG(msg); return(HA_OK); } static int cl_msg_mod(struct ha_msg * msg, const char * name, const void* value, size_t vlen, int type) { int j; int rc; PARANOIDAUDITMSG(msg); if (msg == NULL || name == NULL || value == NULL) { cl_log(LOG_ERR, "cl_msg_mod: NULL input."); return HA_FAIL; } if(type >= DIMOF(fieldtypefuncs)){ cl_log(LOG_ERR, "cl_msg_mod:" "invalid type(%d)", type); return HA_FAIL; } for (j=0; j < msg->nfields; ++j) { if (strcmp(name, msg->names[j]) == 0) { char * newv ; int newlen = vlen; if (type != msg->types[j]){ cl_log(LOG_ERR, "%s: type mismatch(%d %d)", __FUNCTION__, type, msg->types[j]); return HA_FAIL; } newv = fieldtypefuncs[type].dup(value,vlen); if (!newv){ cl_log(LOG_ERR, "duplicating message fields failed" "value=%p, vlen=%d, msg->names[j]=%s", value, (int)vlen, msg->names[j]); return HA_FAIL; } fieldtypefuncs[type].memfree(msg->values[j]); msg->values[j] = newv; msg->vlens[j] = newlen; PARANOIDAUDITMSG(msg); return(HA_OK); } } rc = ha_msg_nadd_type(msg, name,strlen(name), value, vlen, type); PARANOIDAUDITMSG(msg); return rc; } int cl_msg_modstruct(struct ha_msg * msg, const char* name, const struct ha_msg* value) { return cl_msg_mod(msg, name, value, 0, FT_STRUCT); } int cl_msg_modbin(struct ha_msg * msg, const char* name, const void* value, size_t vlen) { return cl_msg_mod(msg, name, value, vlen, FT_BINARY); } int cl_msg_moduuid(struct ha_msg * msg, const char* name, const cl_uuid_t* uuid) { return cl_msg_mod(msg, name, uuid, sizeof(cl_uuid_t), FT_BINARY); } /* Modify the value associated with a particular name */ int cl_msg_modstring(struct ha_msg * msg, const char * name, const char * value) { return cl_msg_mod(msg, name, value, strlen(value), FT_STRING); } /* Return the next message found in the stream */ struct ha_msg * msgfromstream(FILE * f) { char buf[MAXMSGLINE]; char * getsret; clearerr(f); /* Skip until we find a MSG_START (hopefully we skip nothing) */ while(1) { getsret = fgets(buf, sizeof(buf), f); if (!getsret) { break; } if (strcmp(buf, MSG_START) == 0) { return msgfromstream_string(f); } if (strcmp(buf, MSG_START_NETSTRING) == 0){ return msgfromstream_netstring(f); } } return NULL; } /* Return the next message found in the stream with string format */ struct ha_msg * msgfromstream_string(FILE * f) { char buf[MAXMSGLINE]; const char * bufmax = buf + sizeof(buf); struct ha_msg* ret; char * getsret; if ((ret = ha_msg_new(0)) == NULL) { /* Getting an error with EINTR is pretty normal */ /* (so is EOF) */ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN)) && !feof(f)) { cl_log(LOG_ERR, "msgfromstream: cannot get message"); } return(NULL); } /* Add Name=value pairs until we reach MSG_END or EOF */ while(1) { getsret = fgets(buf, MAXMSGLINE, f); if (!getsret) { break; } if (strnlen(buf, MAXMSGLINE) > MAXMSGLINE - 2) { cl_log(LOG_DEBUG , "msgfromstream: field too long [%s]" , buf); } if (!strcmp(buf, MSG_END)) { break; } /* Add the "name=value" string on this line to the message */ if (ha_msg_add_nv(ret, buf, bufmax) != HA_OK) { cl_log(LOG_ERR, "NV failure (msgfromsteam): [%s]" , buf); ha_msg_del(ret); ret=NULL; return(NULL); } } return(ret); } /* Return the next message found in the stream with netstring format*/ struct ha_msg * msgfromstream_netstring(FILE * f) { struct ha_msg * ret; if ((ret = ha_msg_new(0)) == NULL) { /* Getting an error with EINTR is pretty normal */ /* (so is EOF) */ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN)) && !feof(f)) { cl_log(LOG_ERR , "msgfromstream_netstring(): cannot get message"); } return(NULL); } while(1) { char* nvpair; int nvlen; int n; if (fscanf(f, "%d:", &nvlen) <= 0 || nvlen <= 0){ return(ret); } nvpair = malloc(nvlen + 2); if ((n =fread(nvpair, 1, nvlen + 1, f)) != nvlen + 1){ cl_log(LOG_WARNING, "msgfromstream_netstring()" ": Can't get enough nvpair," "expecting %d bytes long, got %d bytes", nvlen + 1, n); ha_msg_del(ret); return(NULL); } process_netstring_nvpair(ret, nvpair, nvlen); } } static gboolean ipc_timer_expired = FALSE; static void cl_sigalarm_handler(int signum) { if (signum == SIGALRM) { ipc_timer_expired = TRUE; } } int cl_ipc_wait_timeout( IPC_Channel *chan, int (*waitfun)(IPC_Channel *chan), unsigned int timeout) { int rc = IPC_FAIL; struct sigaction old_action; memset(&old_action, 0, sizeof(old_action)); cl_signal_set_simple_handler(SIGALRM, cl_sigalarm_handler, &old_action); ipc_timer_expired = FALSE; alarm(timeout); rc = waitfun(chan); if (rc == IPC_INTR && ipc_timer_expired) { rc = IPC_TIMEOUT; } alarm(0); /* ensure it expires */ cl_signal_set_simple_handler(SIGALRM, old_action.sa_handler, &old_action); return rc; } /* Return the next message found in the IPC channel */ static struct ha_msg* msgfromIPC_ll(IPC_Channel * ch, int flag, unsigned int timeout, int *rc_out) { int rc; IPC_Message* ipcmsg; struct ha_msg* hmsg; int need_auth = flag & MSG_NEEDAUTH; int allow_intr = flag & MSG_ALLOWINTR; startwait: if(timeout > 0) { rc = cl_ipc_wait_timeout(ch, ch->ops->waitin, timeout); } else { rc = ch->ops->waitin(ch); } if(rc_out) { *rc_out = rc; } switch(rc) { default: case IPC_FAIL: cl_perror("msgfromIPC: waitin failure"); return NULL; case IPC_TIMEOUT: return NULL; case IPC_BROKEN: sleep(1); return NULL; case IPC_INTR: if ( allow_intr){ goto startwait; }else{ return NULL; } case IPC_OK: break; } ipcmsg = NULL; rc = ch->ops->recv(ch, &ipcmsg); #if 0 if (DEBUGPKTCONT) { cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx" , rc, (unsigned long)ipcmsg); } #endif if(rc_out) { *rc_out = rc; } if (rc != IPC_OK) { return NULL; } hmsg = wirefmt2msg_ll((char *)ipcmsg->msg_body, ipcmsg->msg_len, need_auth); if (ipcmsg->msg_done) { ipcmsg->msg_done(ipcmsg); } AUDITMSG(hmsg); return hmsg; } /* Return the next message found in the IPC channel */ struct ha_msg* msgfromIPC_timeout(IPC_Channel *ch, int flag, unsigned int timeout, int *rc_out) { return msgfromIPC_ll(ch, flag, timeout, rc_out); } struct ha_msg* msgfromIPC(IPC_Channel * ch, int flag) { return msgfromIPC_ll(ch, flag, 0, NULL); } struct ha_msg* msgfromIPC_noauth(IPC_Channel * ch) { int flag = 0; flag |= MSG_ALLOWINTR; return msgfromIPC_ll(ch, flag, 0, NULL); } /* Return the next message found in the IPC channel */ IPC_Message * ipcmsgfromIPC(IPC_Channel * ch) { int rc; IPC_Message* ipcmsg; rc = ch->ops->waitin(ch); switch(rc) { default: case IPC_FAIL: cl_perror("msgfromIPC: waitin failure"); return NULL; case IPC_BROKEN: sleep(1); return NULL; case IPC_INTR: return NULL; case IPC_OK: break; } ipcmsg = NULL; rc = ch->ops->recv(ch, &ipcmsg); #if 0 if (DEBUGPKTCONT) { cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx" , rc, (unsigned long)ipcmsg); } #endif if (rc != IPC_OK) { return NULL; } return(ipcmsg); } /* Writes a message into a stream - used for serial lines */ int msg2stream(struct ha_msg* m, FILE * f) { size_t len; char * s = msg2wirefmt(m, &len); if (s != NULL) { int rc = HA_OK; if (fputs(s, f) == EOF) { rc = HA_FAIL; cl_perror("msg2stream: fputs failure"); } if (fflush(f) == EOF) { cl_perror("msg2stream: fflush failure"); rc = HA_FAIL; } free(s); return(rc); }else{ return(HA_FAIL); } } static void ipcmsg_done(IPC_Message* m); static int clmsg_ipcmsg_allocated = 0; static int clmsg_ipcmsg_freed = 0; void dump_clmsg_ipcmsg_stats(void); void dump_clmsg_ipcmsg_stats(void) { cl_log(LOG_INFO, "clmsg ipcmsg allocated=%d, freed=%d, diff=%d", clmsg_ipcmsg_allocated, clmsg_ipcmsg_freed, clmsg_ipcmsg_allocated - clmsg_ipcmsg_freed); return; } static void ipcmsg_done(IPC_Message* m) { if (!m) { return; } if (m->msg_buf) { free(m->msg_buf); } free(m); m = NULL; clmsg_ipcmsg_freed ++; } /* * create an ipcmsg and copy the data */ IPC_Message* wirefmt2ipcmsg(void* p, size_t len, IPC_Channel* ch) { IPC_Message* ret = NULL; if (p == NULL){ return(NULL); } ret = MALLOCT(IPC_Message); if (!ret) { return(NULL); } memset(ret, 0, sizeof(IPC_Message)); if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) { free(ret); return NULL; } ret->msg_body = (char*)ret->msg_buf + ch->msgpad; memcpy(ret->msg_body, p, len); ret->msg_done = ipcmsg_done; ret->msg_private = NULL; ret->msg_ch = ch; ret->msg_len = len; clmsg_ipcmsg_allocated ++; return ret; } IPC_Message* hamsg2ipcmsg(struct ha_msg* m, IPC_Channel* ch) { size_t len; char * s = msg2wirefmt_ll(m, &len, MSG_NEEDCOMPRESS); IPC_Message* ret = NULL; if (s == NULL) { return ret; } ret = MALLOCT(IPC_Message); if (!ret) { free(s); return ret; } memset(ret, 0, sizeof(IPC_Message)); if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) { free(s); free(ret); return NULL; } ret->msg_body = (char*)ret->msg_buf + ch->msgpad; memcpy(ret->msg_body, s, len); free(s); ret->msg_done = ipcmsg_done; ret->msg_private = NULL; ret->msg_ch = ch; ret->msg_len = len; clmsg_ipcmsg_allocated ++; return ret; } struct ha_msg* ipcmsg2hamsg(IPC_Message*m) { struct ha_msg* ret = NULL; ret = wirefmt2msg(m->msg_body, m->msg_len,MSG_NEEDAUTH); return ret; } int msg2ipcchan(struct ha_msg*m, IPC_Channel*ch) { IPC_Message* imsg; if (m == NULL || ch == NULL) { cl_log(LOG_ERR, "Invalid msg2ipcchan argument"); errno = EINVAL; return HA_FAIL; } if ((imsg = hamsg2ipcmsg(m, ch)) == NULL) { cl_log(LOG_ERR, "hamsg2ipcmsg() failure"); return HA_FAIL; } if (ch->ops->send(ch, imsg) != IPC_OK) { if (ch->ch_status == IPC_CONNECT) { snprintf(ch->failreason,MAXFAILREASON, "send failed,farside_pid=%d, sendq length=%ld(max is %ld)", ch->farside_pid, (long)ch->send_queue->current_qlen, (long)ch->send_queue->max_qlen); } imsg->msg_done(imsg); return HA_FAIL; } return HA_OK; } static gboolean (*msg_authentication_method)(const struct ha_msg* ret) = NULL; void cl_set_oldmsgauthfunc(gboolean (*authfunc)(const struct ha_msg*)) { msg_authentication_method = authfunc; } /* Converts a string (perhaps received via UDP) into a message */ struct ha_msg * string2msg_ll(const char * s, size_t length, int depth, int need_auth) { struct ha_msg* ret; int startlen; int endlen; const char * sp = s; const char * smax = s + length; if ((ret = ha_msg_new(0)) == NULL) { cl_log(LOG_ERR, "%s: creating new msg failed", __FUNCTION__); return(NULL); } startlen = sizeof(MSG_START)-1; if (strncmp(sp, MSG_START, startlen) != 0) { /* This can happen if the sender gets killed */ /* at just the wrong time... */ if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING, "string2msg_ll: no MSG_START"); cl_log(LOG_WARNING, "%s: s=%s", __FUNCTION__, s); cl_log(LOG_WARNING, "depth=%d", depth); } ha_msg_del(ret); return(NULL); }else{ sp += startlen; } endlen = sizeof(MSG_END)-1; /* Add Name=value pairs until we reach MSG_END or end of string */ while (*sp != EOS && strncmp(sp, MSG_END, endlen) != 0) { if (sp >= smax) { cl_log(LOG_ERR, "%s: buffer overflow(sp=%p, smax=%p)", __FUNCTION__, sp, smax); return(NULL); } /* Skip over initial CR/NL things */ sp += strspn(sp, NEWLINE); if (sp >= smax) { cl_log(LOG_ERR, "%s: buffer overflow after NEWLINE(sp=%p, smax=%p)", __FUNCTION__, sp, smax); return(NULL); } /* End of message marker? */ if (strncmp(sp, MSG_END, endlen) == 0) { break; } /* Add the "name=value" string on this line to the message */ if (ha_msg_add_nv_depth(ret, sp, smax, depth) != HA_OK) { if (!cl_msg_quiet_fmterr) { cl_log(LOG_ERR, "NV failure (string2msg_ll):"); cl_log(LOG_ERR, "Input string: [%s]", s); cl_log(LOG_ERR, "sp=%s", sp); cl_log(LOG_ERR, "depth=%d", depth); cl_log_message(LOG_ERR,ret); } ha_msg_del(ret); return(NULL); } if (sp >= smax) { cl_log(LOG_ERR, "%s: buffer overflow after adding field(sp=%p, smax=%p)", __FUNCTION__, sp, smax); return(NULL); } sp += strcspn(sp, NEWLINE); } if (need_auth && msg_authentication_method && !msg_authentication_method(ret)) { const char* from = ha_msg_value(ret, F_ORIG); if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING, "string2msg_ll: node [%s]" " failed authentication", from ? from : "?"); } ha_msg_del(ret); ret = NULL; } return(ret); } struct ha_msg * string2msg(const char * s, size_t length) { return(string2msg_ll(s, length, 0, MSG_NEEDAUTH)); } /* Converts a message into a string (for sending out UDP interface) used in two places: 1.called by msg2string as a implementation for computing string for a message provided the buffer 2.called by is_authentic. In this case, there are no start/end string and the "auth" field is not included in the string */ #define NOROOM { \ cl_log(LOG_ERR, "%s:%d: out of memory bound" \ ", bp=%p, buf + len=%p, len=%ld" \ , __FUNCTION__, __LINE__ \ , bp, buf + len, (long)len); \ cl_log_message(LOG_ERR, m); \ return(HA_FAIL); \ } #define CHECKROOM_CONST(c) CHECKROOM_INT(STRLEN_CONST(c)) #define CHECKROOM_STRING(s) CHECKROOM_INT(strnlen(s, len)) #define CHECKROOM_STRING_INT(s,i) CHECKROOM_INT(strnlen(s, len)+(i)) #define CHECKROOM_INT(i) { \ if ((bp + (i)) > maxp) { \ NOROOM; \ } \ } int msg2string_buf(const struct ha_msg *m, char* buf, size_t len , int depth,int needhead) { char * bp = NULL; int j; char* maxp = buf + len; buf[0]=0; bp = buf; if (needhead){ CHECKROOM_CONST(MSG_START); strcpy(bp, MSG_START); bp += STRLEN_CONST(MSG_START); } for (j=0; j < m->nfields; ++j) { int truelen; int (*tostring)(char*, char*, void*, size_t, int); if (needhead == NOHEAD && strcmp(m->names[j], F_AUTH) == 0) { continue; } if (m->types[j] != FT_STRING){ CHECKROOM_STRING_INT(FT_strings[m->types[j]],2); strcat(bp, "("); bp++; strcat(bp, FT_strings[m->types[j]]); bp++; strcat(bp,")"); bp++; } CHECKROOM_STRING_INT(m->names[j],1); strcat(bp, m->names[j]); bp += m->nlens[j]; strcat(bp, "="); bp++; if(m->types[j] < DIMOF(fieldtypefuncs)){ tostring = fieldtypefuncs[m->types[j]].tostring; } else { cl_log(LOG_ERR, "type(%d) unrecognized", m->types[j]); return HA_FAIL; } if (!tostring || (truelen = tostring(bp, maxp, m->values[j], m->vlens[j], depth)) < 0){ cl_log(LOG_ERR, "tostring failed for field %d", j); return HA_FAIL; } CHECKROOM_INT(truelen+1); bp +=truelen; strcat(bp,"\n"); bp++; } if (needhead){ CHECKROOM_CONST(MSG_END); strcat(bp, MSG_END); bp += strlen(MSG_END); } CHECKROOM_INT(1); bp[0] = EOS; return(HA_OK); } char * msg2string(const struct ha_msg *m) { void *buf; int len; AUDITMSG(m); if (m->nfields <= 0) { cl_log(LOG_ERR, "msg2string: Message with zero fields"); return(NULL); } len = get_stringlen(m); buf = malloc(len); if (buf == NULL) { cl_log(LOG_ERR, "msg2string: no memory for string"); return(NULL); } if (msg2string_buf(m, buf, len ,0, NEEDHEAD) != HA_OK){ cl_log(LOG_ERR, "msg2string: msg2string_buf failed"); free(buf); return(NULL); } return(buf); } gboolean must_use_netstring(const struct ha_msg* msg) { int i; for ( i = 0; i < msg->nfields; i++){ if (msg->types[i] == FT_COMPRESS || msg->types[i] == FT_UNCOMPRESS || msg->types[i] == FT_STRUCT){ return TRUE; } } return FALSE; } #define use_netstring(m) (msgfmt == MSGFMT_NETSTRING || must_use_netstring(m)) static char* msg2wirefmt_ll(struct ha_msg*m, size_t* len, int flag) { int wirefmtlen; int i; int netstg = use_netstring(m); wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m); if (use_traditional_compression &&(flag & MSG_NEEDCOMPRESS) && (wirefmtlen> compression_threshold) && cl_get_compress_fns() != NULL){ return cl_compressmsg(m, len); } if (flag & MSG_NEEDCOMPRESS){ for (i=0 ;i < m->nfields; i++){ int type = m->types[i]; if (fieldtypefuncs[type].prepackaction){ fieldtypefuncs[type].prepackaction(m,i); } } } wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m); if (wirefmtlen >= MAXMSG){ if (flag&MSG_NEEDCOMPRESS) { if (cl_get_compress_fns() != NULL) return cl_compressmsg(m, len); } cl_log(LOG_ERR, "%s: msg too big(%d)", __FUNCTION__, wirefmtlen); return NULL; } if (flag & MSG_NEEDAUTH) { return msg2netstring(m, len); } return msg2wirefmt_noac(m, len); } char* msg2wirefmt(struct ha_msg*m, size_t* len){ return msg2wirefmt_ll(m, len, MSG_NEEDAUTH|MSG_NEEDCOMPRESS); } char* msg2wirefmt_noac(struct ha_msg*m, size_t* len) { if (use_netstring(m)) { return msg2netstring_noauth(m, len); } else { char *tmp; tmp = msg2string(m); if(tmp == NULL){ *len = 0; return NULL; } *len = strlen(tmp) + 1; return tmp; } } static struct ha_msg* wirefmt2msg_ll(const char* s, size_t length, int need_auth) { size_t startlen; struct ha_msg* msg = NULL; startlen = sizeof(MSG_START)-1; if (startlen > length){ return NULL; } if (strncmp( s, MSG_START, startlen) == 0) { msg = string2msg_ll(s, length, 0, need_auth); goto out; } startlen = sizeof(MSG_START_NETSTRING) - 1; if (startlen > length){ return NULL; } if (strncmp(s, MSG_START_NETSTRING, startlen) == 0) { msg = netstring2msg(s, length, need_auth); goto out; } out: if (msg && is_compressed_msg(msg)){ struct ha_msg* ret; if ((ret = cl_decompressmsg(msg))==NULL){ cl_log(LOG_ERR, "decompress msg failed"); ha_msg_del(msg); return NULL; } ha_msg_del(msg); return ret; } return msg; } struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag) { return wirefmt2msg_ll(s, length, flag& MSG_NEEDAUTH); } void cl_log_message (int log_level, const struct ha_msg *m) { int j; if(m == NULL) { cl_log(log_level, "MSG: No message to dump"); return; } cl_log(log_level, "MSG: Dumping message with %d fields", m->nfields); for (j=0; j < m->nfields; ++j) { if(m->types[j] < DIMOF(fieldtypefuncs)){ fieldtypefuncs[m->types[j]].display(log_level, j, m->names[j], m->values[j], m->vlens[j]); } } } #ifdef TESTMAIN_MSGS int main(int argc, char ** argv) { struct ha_msg* m; while (!feof(stdin)) { if ((m=controlfifo2msg(stdin)) != NULL) { fprintf(stderr, "Got message!\n"); if (msg2stream(m, stdout) == HA_OK) { fprintf(stderr, "Message output OK!\n"); }else{ fprintf(stderr, "Could not output Message!\n"); } }else{ fprintf(stderr, "Could not get message!\n"); } } return(0); } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_msg_types.c0000644000000000000000000010417212120057602027024 0ustar00usergroup00000000000000/* * Heartbeat message type functions * * Copyright (C) 2004 Guochun Shi * * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAX # define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif extern const char* FT_strings[]; #define NL_TO_SYM 0 #define SYM_TO_NL 1 static const int SPECIAL_SYMS[MAXDEPTH] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 15, 16, 17, 18, }; #define SPECIAL_SYM 19 struct ha_msg* string2msg_ll(const char*, size_t, int, int); int compose_netstring(char*, const char*, const char*, size_t, size_t*); int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*); int struct_display_print_spaces(char *buffer, int depth); int struct_display_as_xml(int log_level, int depth, struct ha_msg *data, const char *prefix, gboolean formatted); int struct_stringlen(size_t namlen, size_t vallen, const void* value); int struct_netstringlen(size_t namlen, size_t vallen, const void* value); int convert_nl_sym(char* s, int len, char sym, int direction); int bytes_for_int(int x); int bytes_for_int(int x) { int len = 0; if(x < 0) { x = 0-x; len=1; } while(x > 9) { x /= 10; len++; } return len+1; } int netstring_extra(int x) { return (bytes_for_int(x) + x + 2); } int get_netstringlen(const struct ha_msg *m) { int i; int total_len =0 ; if (m == NULL){ cl_log(LOG_ERR, "get_netstringlen:" "asking netstringlen of a NULL message"); return 0; } total_len = sizeof(MSG_START_NETSTRING) + sizeof(MSG_END_NETSTRING) -2 ; for (i = 0; i < m->nfields; i++){ int len; len = fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i], m->vlens[i], m->values[i]); total_len += netstring_extra(len); } return total_len; } int get_stringlen(const struct ha_msg *m) { int i; int total_len =0 ; if (m == NULL){ cl_log(LOG_ERR, "get_stringlen:" "asking stringlen of a NULL message"); return 0; } total_len = sizeof(MSG_START)+sizeof(MSG_END)-1; for (i = 0; i < m->nfields; i++){ total_len += fieldtypefuncs[m->types[i]].stringlen(m->nlens[i], m->vlens[i], m->values[i]); } return total_len; } /* compute the total size of the resulted string if the string list is to be converted */ size_t string_list_pack_length(const GList* _list) { size_t i; GList* list = UNCONST_CAST_POINTER(GList *, _list); size_t total_length = 0; if (list == NULL){ cl_log(LOG_WARNING, "string_list_pack_length():" "list is NULL"); return 0; } for (i = 0; i < g_list_length(list) ; i++){ int len = 0; char * element = g_list_nth_data(list, i); if (element == NULL){ cl_log(LOG_ERR, "string_list_pack_length: " "%luth element of the string list is NULL" , (unsigned long)i); return 0; } len = strlen(element); total_length += bytes_for_int(len) + len + 2; /* 2 is for ":" and "," */ } return total_length ; } /* convert a string list into a single string the format to convert is similar to netstring: ":" "," for example, a list containing two strings "abc" "defg" will be converted into 3:abc,4:defg, @list: the list to be converted @buf: the converted string should be put in the @buf @maxp: max pointer */ int string_list_pack(GList* list, char* buf, char* maxp) { size_t i; char* p = buf; for (i = 0; i < g_list_length(list) ; i++){ char * element = g_list_nth_data(list, i); int element_len; if (element == NULL){ cl_log(LOG_ERR, "string_list_pack: " "%luth element of the string list is NULL" , (unsigned long)i); return 0; } element_len = strlen(element); if (p + 2 + element_len + bytes_for_int(element_len)> maxp){ cl_log(LOG_ERR, "%s: memory out of boundary", __FUNCTION__); return 0; } p += sprintf(p, "%d:%s,", element_len,element); if (p > maxp){ cl_log(LOG_ERR, "string_list_pack: " "buffer overflowed "); return 0; } } return (p - buf); } /* this is reverse process of pack_string_list */ GList* string_list_unpack(const char* packed_str_list, size_t length) { GList* list = NULL; const char* psl = packed_str_list; const char * maxp= packed_str_list + length; int len = 0; while(TRUE){ char* buf; if (*psl == '\0' || psl >= maxp){ break; } if (sscanf( psl, "%d:", &len) <= 0 ){ break; } if (len <=0){ cl_log(LOG_ERR, "unpack_string_list:" "reading len of string error"); if (list){ list_cleanup(list); } return NULL; } while (*psl != ':' && *psl != '\0' ){ psl++; } if (*psl == '\0'){ break; } psl++; buf = malloc(len + 1); if (buf == NULL){ cl_log(LOG_ERR, "unpack_string_list:" "unable to allocate buf"); if(list){ list_cleanup(list); } return NULL; } memcpy(buf, psl, len); buf[len] = '\0'; list = g_list_append(list, buf); psl +=len; if (*psl != ','){ cl_log(LOG_ERR, "unpack_string_list:" "wrong format, s=%s",packed_str_list); } psl++; } return list; } static void string_memfree(void* value) { if (value){ free(value); }else { cl_log(LOG_ERR, "string_memfree: " "value is NULL"); } return; } static void binary_memfree(void* value) { string_memfree(value); } static void struct_memfree( void* value) { struct ha_msg* msg; if (!value){ cl_log(LOG_ERR, "value is NULL"); return ; } msg = (struct ha_msg*) value; ha_msg_del(msg); return ; } static void list_memfree(void* value) { if (!value){ cl_log(LOG_ERR, "value is NULL"); return ; } list_cleanup(value); } static void* binary_dup(const void* value, size_t len) { char* dupvalue; /* 0 byte binary field is allowed*/ if (value == NULL && len > 0){ cl_log(LOG_ERR, "binary_dup:" "NULL value with non-zero len=%d", (int)len); return NULL; } dupvalue = malloc(len + 1); if (dupvalue == NULL){ cl_log(LOG_ERR, "binary_dup:" "malloc failed"); return NULL; } if (value != NULL) { memcpy(dupvalue, value, len); } dupvalue[len] =0; return dupvalue; } static void* string_dup(const void* value, size_t len) { return binary_dup(value, len); } static void* struct_dup(const void* value, size_t len) { char* dupvalue; (void)len; if (!value){ cl_log(LOG_ERR,"struct_dup:" "value is NULL"); return NULL ; } dupvalue = (void*)ha_msg_copy((const struct ha_msg*)value); if (dupvalue == NULL){ cl_log(LOG_ERR, "struct_dup: " "ha_msg_copy failed"); return NULL; } return dupvalue; } static GList* list_copy(const GList* _list) { size_t i; GList* newlist = NULL; GList* list = UNCONST_CAST_POINTER(GList *, _list); for (i = 0; i < g_list_length(list); i++){ char* dup_element = NULL; char* element = g_list_nth_data(list, i); int len; if (element == NULL){ cl_log(LOG_WARNING, "list_copy:" "element is NULL"); continue; } len = strlen(element); dup_element= malloc(len + 1); if ( dup_element == NULL){ cl_log(LOG_ERR, "duplicate element failed"); continue; } memcpy(dup_element, element,len); dup_element[len] = 0; newlist = g_list_append(newlist, dup_element); } return newlist; } static void* list_dup( const void* value, size_t len) { char* dupvalue; (void)len; if (!value){ cl_log(LOG_ERR,"struct_dup:" "value is NULL"); return NULL ; } dupvalue = (void*)list_copy((const GList*)value); if (!dupvalue){ cl_log(LOG_ERR, "list_dup: " "list_copy failed"); return NULL; } return dupvalue; } static void general_display(int log_level, int seq, char* name, void* value, int vlen, int type) { int netslen; int slen; HA_MSG_ASSERT(value); HA_MSG_ASSERT(name); slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value); netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value); cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]", seq, FT_strings[type], name, value, slen, netslen); } static void string_display(int log_level, int seq, char* name, void* value, int vlen) { HA_MSG_ASSERT(name); HA_MSG_ASSERT(value); cl_log(log_level, "MSG[%d] : [%s=%s]", seq, name, (const char*)value); return; } static void binary_display(int log_level, int seq, char* name, void* value, int vlen) { general_display(log_level, seq, name, value, vlen, FT_BINARY); } static void compress_display(int log_level, int seq, char* name, void* value, int vlen){ general_display(log_level, seq, name, value, vlen, FT_COMPRESS); } static void general_struct_display(int log_level, int seq, char* name, void* value, int vlen, int type) { int slen; int netslen; HA_MSG_ASSERT(name); HA_MSG_ASSERT(value); slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value); netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value); cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]", seq, FT_strings[type], name, value, slen, netslen); if(cl_get_string((struct ha_msg*) value, F_XML_TAGNAME) == NULL) { cl_log_message(log_level, (struct ha_msg*) value); } else { /* use a more friendly output format for nested messages */ struct_display_as_xml(log_level, 0, value, NULL, TRUE); } } static void struct_display(int log_level, int seq, char* name, void* value, int vlen) { general_struct_display(log_level, seq, name, value, vlen, FT_STRUCT); } static void uncompress_display(int log_level, int seq, char* name, void* value, int vlen) { general_struct_display(log_level, seq, name, value, vlen, FT_UNCOMPRESS); } #define update_buffer_head(buffer, len) if(len < 0) { \ (*buffer) = EOS; return -1; \ } else { \ buffer += len; \ } int struct_display_print_spaces(char *buffer, int depth) { int lpc = 0; int spaces = 2*depth; /* <= so that we always print 1 space - prevents problems with syslog */ for(lpc = 0; lpc <= spaces; lpc++) { if(sprintf(buffer, "%c", ' ') < 1) { return -1; } buffer += 1; } return lpc; } int struct_display_as_xml( int log_level, int depth, struct ha_msg *data, const char *prefix, gboolean formatted) { int lpc = 0; int printed = 0; gboolean has_children = FALSE; char print_buffer[1000]; char *buffer = print_buffer; const char *name = cl_get_string(data, F_XML_TAGNAME); if(data == NULL) { return 0; } else if(name == NULL) { cl_log(LOG_WARNING, "Struct at depth %d had no name", depth); cl_log_message(log_level, data); return 0; } if(formatted) { printed = struct_display_print_spaces(buffer, depth); update_buffer_head(buffer, printed); } printed = sprintf(buffer, "<%s", name); update_buffer_head(buffer, printed); for (lpc = 0; lpc < data->nfields; lpc++) { const char *prop_name = data->names[lpc]; const char *prop_value = data->values[lpc]; if(data->types[lpc] != FT_STRING) { continue; } else if(prop_name == NULL) { continue; } else if(prop_name[0] == '_' && prop_name[1] == '_') { continue; } printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head(buffer, printed); } for (lpc = 0; lpc < data->nfields; lpc++) { if(data->types[lpc] == FT_STRUCT) { has_children = TRUE; break; } } printed = sprintf(buffer, "%s>", has_children==0?"/":""); update_buffer_head(buffer, printed); cl_log(log_level, "%s%s", prefix?prefix:"", print_buffer); buffer = print_buffer; if(has_children == FALSE) { return 0; } for (lpc = 0; lpc < data->nfields; lpc++) { if(data->types[lpc] != FT_STRUCT) { continue; } else if(0 > struct_display_as_xml( log_level, depth+1, data->values[lpc], prefix, formatted)) { return -1; } } if(formatted) { printed = struct_display_print_spaces(buffer, depth); update_buffer_head(buffer, printed); } cl_log(log_level, "%s%s", prefix?prefix:"", print_buffer, name); return 0; } static int liststring(GList* list, char* buf, int maxlen) { char* p = buf; char* maxp = buf + maxlen; size_t i; for ( i = 0; i < g_list_length(list); i++){ char* element = g_list_nth_data(list, i); if (element == NULL) { cl_log(LOG_ERR, "%luth element is NULL " , (unsigned long)i); return HA_FAIL; } else{ if (i == 0){ p += sprintf(p,"%s",element); }else{ p += sprintf(p," %s",element); } } if ( p > maxp){ cl_log(LOG_ERR, "buffer overflow"); return HA_FAIL; } } return HA_OK; } static void list_display(int log_level, int seq, char* name, void* value, int vlen) { GList* list; char buf[MAXLENGTH]; HA_MSG_ASSERT(name); HA_MSG_ASSERT(value); list = value; if (liststring(list, buf, MAXLENGTH) != HA_OK){ cl_log(LOG_ERR, "liststring error"); return; } cl_log(log_level, "MSG[%d] :[(%s)%s=%s]", seq, FT_strings[FT_LIST], name, buf); return ; } /* * This function changes each new line in the input string * into a special symbol, or the other way around */ int convert_nl_sym(char* s, int len, char sym, int direction) { int i; if (direction != NL_TO_SYM && direction != SYM_TO_NL){ cl_log(LOG_ERR, "convert_nl_sym(): direction not defined!"); return(HA_FAIL); } for (i = 0; i < len && s[i] != EOS; i++){ switch(direction){ case NL_TO_SYM : if (s[i] == '\n'){ s[i] = sym; break; } if (s[i] == sym){ cl_log(LOG_ERR , "convert_nl_sym(): special symbol \'0x%x\' (%c) found" " in string at %d (len=%d)", s[i], s[i], i, len); i -= 10; if(i < 0) { i = 0; } cl_log(LOG_ERR, "convert_nl_sym(): %s", s + i); return(HA_FAIL); } break; case SYM_TO_NL: if (s[i] == sym){ s[i] = '\n'; break; } break; default: /* nothing, never executed*/; } } return(HA_OK); } /* * This function changes each new line in the input string * into a special symbol, or the other way around */ static int convert(char* s, int len, int depth, int direction) { if (direction != NL_TO_SYM && direction != SYM_TO_NL){ cl_log(LOG_ERR, "convert(): direction not defined!"); return(HA_FAIL); } if (depth >= MAXDEPTH ){ cl_log(LOG_ERR, "convert(): MAXDEPTH exceeded: %d", depth); return(HA_FAIL); } return convert_nl_sym(s, len, SPECIAL_SYMS[depth], direction); } static int string_stringlen(size_t namlen, size_t vallen, const void* value) { HA_MSG_ASSERT(value); /* HA_MSG_ASSERT( vallen == strlen(value)); */ return namlen + vallen + 2; } static int binary_netstringlen(size_t namlen, size_t vallen, const void* value) { int length; HA_MSG_ASSERT(value); length = 3 + namlen + 1 + vallen; return length; } static int string_netstringlen(size_t namlen, size_t vallen, const void* value) { HA_MSG_ASSERT(value); HA_MSG_ASSERT( vallen == strlen(value)); return binary_netstringlen(namlen, vallen, value); } static int binary_stringlen(size_t namlen, size_t vallen, const void* value) { HA_MSG_ASSERT(value); return namlen + B64_stringlen(vallen) + 2 + 3; /*overhead 3 is for type*/ } int struct_stringlen(size_t namlen, size_t vallen, const void* value) { const struct ha_msg* childmsg; HA_MSG_ASSERT(value); (void)vallen; childmsg = (const struct ha_msg*)value; return namlen +2 + 3 + get_stringlen(childmsg); /*overhead 3 is for type*/ } int struct_netstringlen(size_t namlen, size_t vallen, const void* value) { int ret; const struct ha_msg* childmsg; int len; HA_MSG_ASSERT(value); (void)vallen; childmsg = (const struct ha_msg*)value; len = get_netstringlen(childmsg); ret = 3 + namlen + 1 + len; return ret; } static int list_stringlen(size_t namlen, size_t vallen, const void* value) { (void)value; return namlen + vallen + 2 + 3; /*overhead 3 is for type (FT_STRUCT)*/ } static int list_netstringlen(size_t namlen, size_t vallen, const void* value) { int ret; const GList* list; list = (const GList*)value; ret = 3 + namlen + 1 + string_list_pack_length(list); return ret; } static int add_binary_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { int next; if ( !msg || !name || !value || depth < 0){ cl_log(LOG_ERR, "add_binary_field:" " invalid input argument"); return HA_FAIL; } next = msg->nfields; msg->names[next] = name; msg->nlens[next] = namelen; msg->values[next] = value; msg->vlens[next] = vallen; msg->types[next] = FT_BINARY; msg->nfields++; return HA_OK; } static int add_struct_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { int next; if ( !msg || !name || !value || depth < 0){ cl_log(LOG_ERR, "add_struct_field:" " invalid input argument"); return HA_FAIL; } next = msg->nfields; msg->names[next] = name; msg->nlens[next] = namelen; msg->values[next] = value; msg->vlens[next] = vallen; msg->types[next] = FT_STRUCT; msg->nfields++; return HA_OK; } static int add_list_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { int next; int j; GList* list = NULL; if ( !msg || !name || !value || namelen <= 0 || vallen <= 0 || depth < 0){ cl_log(LOG_ERR, "add_list_field:" " invalid input argument"); return HA_FAIL; } for (j=0; j < msg->nfields; ++j) { if (strcmp(name, msg->names[j]) == 0) { break; } } if ( j >= msg->nfields){ list = (GList*)value; next = msg->nfields; msg->names[next] = name; msg->nlens[next] = namelen; msg->values[next] = value; msg->vlens[next] = vallen; msg->types[next] = FT_LIST; msg->nfields++; } else if( msg->types[j] == FT_LIST ){ GList* oldlist = (GList*) msg->values[j]; int listlen; size_t i; for ( i =0; i < g_list_length((GList*)value); i++){ list = g_list_append(oldlist, g_list_nth_data((GList*)value, i)); } if (list == NULL){ cl_log(LOG_ERR, "add_list_field:" " g_list_append() failed"); return HA_FAIL; } listlen = string_list_pack_length(list); msg->values[j] = list; msg->vlens[j] = listlen; g_list_free((GList*)value); /*we don't free each element because they are used in new list*/ free(name); /* this name is no longer necessary because msg->names[j] is reused */ } else { cl_log(LOG_ERR, "field already exists " "with differnt type=%d", msg->types[j]); return (HA_FAIL); } return HA_OK; } static int add_compress_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { int next; if ( !msg || !name || !value || depth < 0){ cl_log(LOG_ERR, "add_binary_field:" " invalid input argument"); return HA_FAIL; } next = msg->nfields; msg->names[next] = name; msg->nlens[next] = namelen; msg->values[next] = value; msg->vlens[next] = vallen; msg->types[next] = FT_COMPRESS; msg->nfields++; return HA_OK; } static int add_uncompress_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { int next; if ( !msg || !name || !value || depth < 0){ cl_log(LOG_ERR, "add_struct_field:" " invalid input argument"); return HA_FAIL; } next = msg->nfields; msg->names[next] = name; msg->nlens[next] = namelen; msg->values[next] = value; msg->vlens[next] = vallen; msg->types[next] = FT_UNCOMPRESS; msg->nfields++; return HA_OK; } /*print a string to a string, pretty simple one :) */ static int str2string(char* buf, char* maxp, void* value, size_t len, int depth) { char* s = value; char* p = buf; (void)maxp; (void)depth; if (buf + len > maxp){ cl_log(LOG_ERR, "%s: out of boundary", __FUNCTION__); return -1; } if ( strlen(s) != len){ cl_log(LOG_ERR, "str2string:" "the input len != string length"); return -1; } strcat(buf, s); while(*p != '\0'){ if (*p == '\n'){ *p = SPECIAL_SYM; } p++; } return len; } /*print a binary value to a string using base64 library */ static int binary2string(char* buf, char* maxp, void* value, size_t len, int depth) { int baselen; int truelen = 0; (void)depth; baselen = B64_stringlen(len) + 1; if ( buf + baselen > maxp){ cl_log(LOG_ERR, "binary2string: out of bounary"); return -1; } truelen = binary_to_base64(value, len, buf, baselen); return truelen; } /*print a struct(ha_msg) to a string @depth denotes the number of recursion */ static int struct2string(char* buf, char* maxp, void* value, size_t len, int depth) { struct ha_msg* msg = value; int baselen = get_stringlen(msg); (void)len; if ( buf + baselen > maxp){ cl_log(LOG_ERR, "struct2string: not enough buffer" "for the struct to generate a string"); return -1; } if (msg2string_buf(msg, buf ,baselen,depth + 1, NEEDHEAD) != HA_OK){ cl_log(LOG_ERR , "struct2string(): msg2string_buf for" " child message failed"); return -1; } if (convert(buf, baselen, depth, NL_TO_SYM) != HA_OK){ cl_log(LOG_ERR , "struct2string(): convert failed"); return -1; } return strlen(buf); } /* print a list to a string */ static int list2string(char* buf, char* maxp, void* value, size_t len, int depth) { int listlen; GList* list = (GList*) value; (void)len; (void)depth; listlen = string_list_pack(list , buf, maxp); if (listlen == 0){ cl_log(LOG_ERR, "list2string():" "string_list_pack() failed"); return -1; } return listlen; } static int string2str(void* value, size_t len, int depth, void** nv, size_t* nlen ) { if (!value || !nv || !nlen || depth < 0){ cl_log(LOG_ERR, "string2str:invalid input"); return HA_FAIL; } if (convert_nl_sym(value, len, SPECIAL_SYM, SYM_TO_NL) != HA_OK){ cl_log(LOG_ERR, "string2str:convert_nl_sym" "from symbol to new line failed"); return HA_FAIL; } *nv = value; *nlen = len; return HA_OK; } static int string2binary(void* value, size_t len, int depth, void** nv, size_t* nlen) { char tmpbuf[MAXLINE]; char* buf = NULL; int buf_malloced = 0; int ret = HA_FAIL; if (len > MAXLINE){ buf = malloc(len); if (buf == NULL){ cl_log(LOG_ERR, "%s: malloc failed", __FUNCTION__); goto out; } buf_malloced = 1; }else { buf = &tmpbuf[0]; } if (value == NULL && len == 0){ *nv = NULL; *nlen = 0; ret = HA_OK; goto out; } if ( !value || !nv || depth < 0){ cl_log(LOG_ERR, "string2binary:invalid input"); ret = HA_FAIL; goto out; } memcpy(buf, value, len); *nlen = base64_to_binary(buf, len, value, len); *nv = value; ret = HA_OK; out: if (buf_malloced && buf){ free(buf); } return ret; } static int string2struct(void* value, size_t vallen, int depth, void** nv, size_t* nlen) { struct ha_msg *tmpmsg; if (!value || !nv || depth < 0){ cl_log(LOG_ERR, "string2struct:invalid input"); return HA_FAIL; } if (convert(value, vallen, depth,SYM_TO_NL) != HA_OK){ cl_log(LOG_ERR , "ha_msg_addraw_ll(): convert failed"); return(HA_FAIL); } tmpmsg = string2msg_ll(value, vallen,depth + 1, 0); if (tmpmsg == NULL){ cl_log(LOG_ERR , "string2struct()" ": string2msg_ll failed"); return(HA_FAIL); } free(value); *nv = tmpmsg; *nlen = 0; return HA_OK; } static int string2list(void* value, size_t vallen, int depth, void** nv, size_t* nlen) { GList* list; if (!value || !nv || !nlen || depth < 0){ cl_log(LOG_ERR, "string2struct:invalid input"); return HA_FAIL; } list = string_list_unpack(value, vallen); if (list == NULL){ cl_log(LOG_ERR, "ha_msg_addraw_ll():" "unpack_string_list failed: %s", (char*)value); return(HA_FAIL); } free(value); *nv = (void*)list; *nlen = string_list_pack_length(list); return HA_OK; } static int fields2netstring(char* sp, char* smax, char* name, size_t nlen, void* value, size_t vallen, int type, size_t* comlen) { size_t fieldlen; size_t slen; int ret = HA_OK; char* sp_save = sp; char* tmpsp; fieldlen = fieldtypefuncs[type].netstringlen(nlen, vallen, value); /* this check seems to be superfluous because of the next one if (fieldlen > MAXMSG){ cl_log(LOG_INFO, "%s: field too big(%d)", __FUNCTION__, (int)fieldlen); return HA_FAIL; } */ tmpsp = sp + netstring_extra(fieldlen); if (tmpsp > smax){ cl_log(LOG_ERR, "%s: memory out of boundary, tmpsp=%p, smax=%p", __FUNCTION__, tmpsp, smax); return HA_FAIL; } sp += sprintf(sp , "%d:(%d)%s=", (int)fieldlen, type, name); switch (type){ case FT_STRING: case FT_BINARY: case FT_COMPRESS: memcpy(sp, value, vallen); slen = vallen; break; case FT_UNCOMPRESS: case FT_STRUCT: { struct ha_msg* msg = (struct ha_msg*) value; /* infinite recursion? Must say that I got lost at * this point */ ret = msg2netstring_buf(msg, sp,get_netstringlen(msg), &slen); break; } case FT_LIST: { char buf[MAXLENGTH]; GList* list = NULL; int tmplen; list = (GList*) value; tmplen = string_list_pack_length(list); if (tmplen >= MAXLENGTH){ cl_log(LOG_ERR, "string list length exceeds limit"); return(HA_FAIL); } if (string_list_pack(list, buf, buf + MAXLENGTH) != tmplen ){ cl_log(LOG_ERR, "packing string list return wrong length"); return(HA_FAIL); } memcpy(sp, buf, tmplen); slen = tmplen; ret = HA_OK; break; } default: ret = HA_FAIL; cl_log(LOG_ERR, "%s: Wrong type (%d)", __FUNCTION__,type); } if (ret == HA_FAIL){ return ret; } sp +=slen; *sp++ = ','; *comlen = sp - sp_save; return HA_OK; } static int netstring2string(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen) { char* dupvalue; if (value == NULL && vlen == 0){ *retvalue = NULL; *ret_vlen = 0; return HA_OK; } if ( !value || !retvalue || !ret_vlen){ cl_log(LOG_ERR, " netstring2string:" "invalid input arguments"); return HA_FAIL; } dupvalue = binary_dup(value, vlen); if (!dupvalue){ cl_log(LOG_ERR, "netstring2string:" "duplicating value failed"); return HA_FAIL; } *retvalue = dupvalue; *ret_vlen = vlen; return HA_OK; } static int netstring2binary(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen) { return netstring2string(value, vlen, retvalue, ret_vlen); } static int netstring2struct(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen) { struct ha_msg* msg; if ( !value || !retvalue || !ret_vlen){ cl_log(LOG_ERR, " netstring2struct:" "invalid input arguments"); return HA_FAIL; } msg = netstring2msg(value, vlen, 0); if (!msg){ cl_log(LOG_ERR, "netstring2struct:" "netstring2msg failed"); return HA_FAIL; } *retvalue =(void* ) msg; *ret_vlen = 0; return HA_OK; } static int netstring2list(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen) { GList* list; if ( !value || !retvalue || !ret_vlen){ cl_log(LOG_ERR, " netstring2struct:" "invalid input arguments"); return HA_FAIL; } list = string_list_unpack(value, vlen); if (list == NULL){ cl_log(LOG_ERR, "netstring2list: unpacking string list failed"); cl_log(LOG_INFO, "thisbuf=%s", (const char*)value); return HA_FAIL; } *retvalue = (void*)list; *ret_vlen = string_list_pack_length(list); return HA_OK; } static int add_string_field(struct ha_msg* msg, char* name, size_t namelen, void* value, size_t vallen, int depth) { size_t internal_type; unsigned long tmptype; char *cp_name = NULL; size_t cp_namelen; size_t cp_vallen; void *cp_value = NULL; int next; if ( !msg || !name || !value || namelen <= 0 || depth < 0){ cl_log(LOG_ERR, "add_string_field:" " invalid input argument"); return HA_FAIL; } internal_type = FT_STRING; if (name[0] == '('){ int nlo = 3; /*name length overhead */ if (name[2] != ')'){ if (!cl_msg_quiet_fmterr) { cl_log(LOG_ERR , "ha_msg_addraw_ll(): no closing parentheses"); } return(HA_FAIL); } tmptype = name[1] - '0'; if (tmptype < 0 || tmptype > 9) { cl_log(LOG_ERR , "ha_msg_addraw_ll(): not a number."); return(HA_FAIL); } internal_type = tmptype; if (internal_type == FT_STRING){ cl_log(LOG_ERR , "ha_msg_addraw_ll(): wrong type"); return(HA_FAIL); } cp_name = name; cp_namelen = namelen - nlo ; memmove(cp_name, name + nlo, namelen - nlo); cp_name[namelen - nlo] = EOS; }else { cp_name = name; cp_namelen = namelen; } if(internal_type < DIMOF(fieldtypefuncs)){ int (*stringtofield)(void*, size_t, int depth, void**, size_t* ); int (*fieldstringlen)( size_t, size_t, const void*); stringtofield= fieldtypefuncs[internal_type].stringtofield; if (!stringtofield || stringtofield(value, vallen, depth, &cp_value, &cp_vallen) != HA_OK){ cl_log(LOG_ERR, "add_string_field: stringtofield failed"); return HA_FAIL; } fieldstringlen = fieldtypefuncs[internal_type].stringlen; if (!fieldstringlen || fieldstringlen(cp_namelen, cp_vallen, cp_value) <= 0 ){ cl_log(LOG_ERR, "add_string_field: stringlen failed"); return HA_FAIL; } } else { cl_log(LOG_ERR, "add_string_field():" " wrong type %lu", (unsigned long)internal_type); return HA_FAIL; } next = msg->nfields; msg->values[next] = cp_value; msg->vlens[next] = cp_vallen; msg->names[next] = cp_name; msg->nlens[next] = cp_namelen; msg->types[next] = internal_type; msg->nfields++; return HA_OK; } static int uncompress2compress(struct ha_msg* msg, int index) { char* buf; size_t buflen = MAXMSG; int rc = HA_FAIL; buf = malloc(buflen); if (!buf) { cl_log(LOG_ERR, "%s: failed to allocate buffer", __FUNCTION__); goto err; } if (msg->types[index] != FT_UNCOMPRESS){ cl_log(LOG_ERR, "%s: the %dth field is not FT_UNCOMPRESS type", __FUNCTION__, index); goto err; } if (cl_compress_field(msg, index, buf, &buflen) != HA_OK){ cl_log(LOG_ERR, "%s: compressing %dth field failed", __FUNCTION__, index); goto err; } rc = cl_msg_replace(msg, index, buf, buflen, FT_COMPRESS); err: if (buf) { free(buf); } return rc; } static int compress2uncompress(struct ha_msg* msg, int index) { char *buf = NULL; size_t buflen = MAXUNCOMPRESSED; struct ha_msg* msgfield; int err = HA_FAIL; buf = malloc(buflen); if (!buf) { cl_log(LOG_ERR, "%s: allocating buffer for uncompression failed", __FUNCTION__); goto out; } if (cl_decompress_field(msg, index, buf, &buflen) != HA_OK){ cl_log(LOG_ERR, "%s: compress field failed", __FUNCTION__); goto out; } msgfield = wirefmt2msg(buf, buflen, 0); if (msgfield == NULL){ cl_log(LOG_ERR, "%s: wirefmt to msg failed", __FUNCTION__); goto out; } err = cl_msg_replace(msg, index, (char*)msgfield, 0, FT_UNCOMPRESS); ha_msg_del(msgfield); out: if (buf) { free(buf); } return err; } /* * string FT_STRING * string is the basic type used in heartbeat, it is used for printable ascii value * * binary FT_BINARY * binary means the value can be any binary value, including non-printable ascii value * * struct FT_STRUCT * struct means the value is also an ha_msg (actually it is a pointer to an ha message) * * list FT_LIST * LIST means the value is a GList. Right now we only suppport a Glist of strings * * compress FT_COMPRESS * This field and the next one(FT_UNCOMPRESS) is designed to optimize compression in message * (see cl_compression.c for more about compression). This field is similar to the binary field. * It stores a compressed field, which will be an ha_msg if uncompressed. Most of time this field * act like a binary field until compress2uncompress() is called. That function will be called * when someone calls cl_get_struct() to get this field value. After that this field is converted * to a new type FT_UNCOMPRESS * * uncompress FT_UNCOMPRESS * As said above, this field is used to optimize compression. This field is similar to the struct * field. It's value is a pointer to an ha_msg. This field will be converted to a new type FT_COMPRESS * when msg2wirefmt() is called, where uncompress2compress is called to do the field compression */ struct fieldtypefuncs_s fieldtypefuncs[NUM_MSG_TYPES]= { {string_memfree, string_dup, string_display, add_string_field, string_stringlen,string_netstringlen, str2string,fields2netstring, string2str, netstring2string, NULL, NULL}, {binary_memfree, binary_dup, binary_display, add_binary_field, binary_stringlen,binary_netstringlen, binary2string,fields2netstring, string2binary, netstring2binary, NULL, NULL}, {struct_memfree, struct_dup, struct_display, add_struct_field, struct_stringlen, struct_netstringlen, struct2string, fields2netstring, \ string2struct, netstring2struct, NULL, NULL}, {list_memfree, list_dup, list_display, add_list_field, list_stringlen, list_netstringlen, list2string, fields2netstring, string2list, netstring2list, NULL, NULL}, {binary_memfree, binary_dup, compress_display, add_compress_field, binary_stringlen,binary_netstringlen, binary2string ,fields2netstring, string2binary , netstring2binary, NULL, compress2uncompress}, /*FT_COMPRESS*/ {struct_memfree, struct_dup, uncompress_display, add_uncompress_field, struct_stringlen, struct_netstringlen, NULL , fields2netstring, NULL , netstring2struct, uncompress2compress, NULL}, /*FT_UNCOMPRSS*/ }; Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_netstring.c0000644000000000000000000002720612120057602027031 0ustar00usergroup00000000000000/* * netstring implementation * * Copyright (c) 2003 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Avoid sprintf. Use snprintf instead, even if you count your bytes. * It can detect calculation errors (if used properly) * and will not make the security audit tools crazy. */ #define MAX_AUTH_BYTES 64 int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*); int compose_netstring(char*, const char*, const char*, size_t, size_t*); int is_auth_netstring(const char*, size_t, const char*, size_t); char* msg2netstring(const struct ha_msg*, size_t*); int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen); extern int bytes_for_int(int x); extern const char * FT_strings[]; static int (*authmethod)(int whichauth , const void * data , size_t datalen , char * authstr , size_t authlen) = NULL; void cl_set_authentication_computation_method(int (*method)(int whichauth , const void * data , size_t datalen , char * authstr , size_t authlen)) { authmethod = method; } int cl_parse_int(const char *sp, const char *smax, int* len); int cl_parse_int(const char *sp, const char *smax, int* len) { char ch = 0; int offset = 0; *len = 0; errno = 0; for( ; sp+offset < smax; offset++) { ch = sp[offset] - '0'; if(ch > 9) { /* ch >= 0 is implied by the data type*/ break; } *len *= 10; *len += ch; } if(offset == 0) { cl_log(LOG_ERR, "cl_parse_int: Couldn't parse an int from: %.5s", sp); } return offset; } int compose_netstring(char * s, const char * smax, const char* data, size_t len, size_t* comlen) { char * sp = s; /* 2 == ":" + "," */ if (s + len + 2 + bytes_for_int(len) > smax) { cl_log(LOG_ERR, "netstring pointer out of boundary(compose_netstring)"); return(HA_FAIL); } sp += sprintf(sp, "%ld:", (long)len); if(data){ memcpy(sp, data, len); } sp += len; *sp++ = ','; *comlen = sp - s; return(HA_OK); } /* Converts a message into a netstring */ int msg2netstring_buf(const struct ha_msg *m, char *s, size_t buflen, size_t * slen) { int i; char * sp; char * smax; int ret = HA_OK; sp = s; smax = s + buflen; strcpy(sp, MSG_START_NETSTRING); sp += strlen(MSG_START_NETSTRING); for (i=0; i < m->nfields; i++) { size_t flen; int tmplen; /* some of these functions in its turn invoke us again */ ret = fieldtypefuncs[m->types[i]].tonetstring(sp, smax, m->names[i], m->nlens[i], m->values[i], m->vlens[i], m->types[i], &flen); if (ret != HA_OK){ cl_log(LOG_ERR, "encoding msg to netstring failed"); cl_log_message(LOG_ERR, m); return ret; } tmplen = netstring_extra(fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i], m->vlens[i], m->values[i])); if (flen != tmplen ){ cl_log(LOG_ERR,"netstring len discrepency: actual usage is %d bytes" "it should use %d", (int)flen, tmplen); } sp +=flen; } if (sp + strlen(MSG_END_NETSTRING) > smax){ cl_log(LOG_ERR, "%s: out of boundary for MSG_END_NETSTRING", __FUNCTION__); return HA_FAIL; } strcpy(sp, MSG_END_NETSTRING); sp += sizeof(MSG_END_NETSTRING) -1; if (sp > smax){ cl_log(LOG_ERR, "msg2netstring: exceed memory boundary sp =%p smax=%p", sp, smax); return(HA_FAIL); } *slen = sp - s; return(HA_OK); } int get_netstringlen_auth(const struct ha_msg* m); int get_netstringlen_auth(const struct ha_msg* m) { int len = get_netstringlen(m) + MAX_AUTH_BYTES; return len; } static char * msg2netstring_ll(const struct ha_msg *m, size_t * slen, int need_auth) { int len; char* s; int authnum; char authtoken[MAXLINE]; char authstring[MAXLINE]; char* sp; size_t payload_len; char* smax; len= get_netstringlen_auth(m) + 1; /* use MAXUNCOMPRESSED for the in memory size check */ if (len >= MAXUNCOMPRESSED){ cl_log(LOG_ERR, "%s: msg is too large; len=%d," " MAX msg allowed=%d", __FUNCTION__, len, MAXUNCOMPRESSED); return NULL; } s = calloc(1, len); if (!s){ cl_log(LOG_ERR, "%s: no memory for netstring", __FUNCTION__); return(NULL); } smax = s + len; if (msg2netstring_buf(m, s, len, &payload_len) != HA_OK){ cl_log(LOG_ERR, "%s: msg2netstring_buf() failed", __FUNCTION__); free(s); return(NULL); } sp = s + payload_len; if ( need_auth && authmethod){ int auth_strlen; authnum = authmethod(-1, s, payload_len, authtoken,sizeof(authtoken)); if (authnum < 0){ cl_log(LOG_WARNING , "Cannot compute message authentication!"); free(s); return(NULL); } sprintf(authstring, "%d %s", authnum, authtoken); auth_strlen = strlen(authstring); if (sp + 2 + auth_strlen + bytes_for_int(auth_strlen) >= smax){ cl_log(LOG_ERR, "%s: out of boundary for auth", __FUNCTION__); free(s); return NULL; } sp += sprintf(sp, "%ld:%s,", (long)strlen(authstring), authstring); } *slen = sp - s; return(s); } char * msg2netstring(const struct ha_msg *m, size_t * slen) { char* ret; ret = msg2netstring_ll(m, slen, TRUE); return ret; } char * msg2netstring_noauth(const struct ha_msg *m, size_t * slen) { char * ret; ret = msg2netstring_ll(m, slen, FALSE); return ret; } /* * Peel one string off in a netstring */ static int peel_netstring(const char * s, const char * smax, int* len, const char ** data, int* parselen ) { int offset = 0; const char * sp = s; if (sp >= smax){ return(HA_FAIL); } offset = cl_parse_int(sp, smax, len); if (*len < 0 || offset <= 0){ cl_log(LOG_ERR, "peel_netstring: Couldn't parse an int starting at: %.5s", sp); return(HA_FAIL); } sp = sp+offset; while (*sp != ':' && sp < smax) { sp ++; } if (sp >= smax) { return(HA_FAIL); } sp ++; *data = sp; sp += (*len); if (sp >= smax) { return(HA_FAIL); } if (*sp != ','){ return(HA_FAIL); } sp++; *parselen = sp - s; return(HA_OK); } int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen) { const char *name; int nlen; const char *ns_value; int ns_vlen; void *value; size_t vlen; int type; void (*memfree)(void*); int ret = HA_OK; assert(*nvpair == '('); nvpair++; type = nvpair[0] - '0'; nvpair++; /* if this condition is no longer true, change the above to: * nvpair += cl_parse_int(nvpair, nvpair+nvlen, &type) */ assert(type >= 0 && type < 10); assert(*nvpair == ')'); nvpair++; if ((nlen = strcspn(nvpair, EQUAL)) <= 0 || nvpair[nlen] != '=') { if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING , "%s: line doesn't contain '='", __FUNCTION__); cl_log(LOG_INFO, "%s", nvpair); } return(HA_FAIL); } name = nvpair; ns_value = name +nlen + 1; ns_vlen = nvpair + nvlen - ns_value -3 ; if (fieldtypefuncs[type].netstringtofield(ns_value,ns_vlen, &value, &vlen) != HA_OK){ cl_log(LOG_ERR, "netstringtofield failed in %s", __FUNCTION__); return HA_FAIL; } memfree = fieldtypefuncs[type].memfree; if (ha_msg_nadd_type(m , name, nlen, value, vlen,type) != HA_OK) { cl_log(LOG_ERR, "ha_msg_nadd fails(netstring2msg_rec)"); ret = HA_FAIL; } if (memfree && value){ memfree(value); } else{ cl_log(LOG_ERR, "netstring2msg_rec:" "memfree or ret_value is NULL"); ret= HA_FAIL; } return ret; } /* Converts a netstring into a message*/ static struct ha_msg * netstring2msg_rec(const char *s, size_t length, int* slen) { struct ha_msg* ret = NULL; const char * sp = s; const char * smax = s + length; int startlen; int endlen; if ((ret = ha_msg_new(0)) == NULL){ return(NULL); } startlen = sizeof(MSG_START_NETSTRING)-1; if (strncmp(sp, MSG_START_NETSTRING, startlen) != 0) { /* This can happen if the sender gets killed */ /* at just the wrong time... */ if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING, "netstring2msg_rec: no MSG_START"); ha_msg_del(ret); } return(NULL); }else{ sp += startlen; } endlen = sizeof(MSG_END_NETSTRING) - 1; while (sp < smax && strncmp(sp, MSG_END_NETSTRING, endlen) !=0 ){ const char *nvpair; int nvlen; int parselen; if (peel_netstring(sp , smax, &nvlen, &nvpair,&parselen) != HA_OK){ cl_log(LOG_ERR , "%s:peel_netstring fails for name/value pair", __FUNCTION__); cl_log(LOG_ERR, "sp=%s", sp); ha_msg_del(ret); return(NULL); } sp += parselen; if (process_netstring_nvpair(ret, nvpair, nvlen) != HA_OK){ cl_log(LOG_ERR, "%s: processing nvpair failed", __FUNCTION__); return HA_FAIL; } } sp += sizeof(MSG_END_NETSTRING) -1; *slen = sp - s; return(ret); } struct ha_msg * netstring2msg(const char* s, size_t length, int needauth) { const char *sp; struct ha_msg *msg; const char *smax = s + length; int parselen; int authlen; const char *authstring; /*actual string length used excluding auth string*/ int slen = 0; /* assign to keep compiler happy */ msg = netstring2msg_rec(s, length, &slen); if (needauth == FALSE || !authmethod){ goto out; } sp = s + slen; if (peel_netstring(sp , smax, &authlen, &authstring, &parselen) !=HA_OK){ cl_log(LOG_ERR, "peel_netstring() error in getting auth string"); cl_log(LOG_ERR, "sp=%s", sp); cl_log(LOG_ERR, "s=%s", s); ha_msg_del(msg); return(NULL); } if (sp + parselen > smax){ cl_log(LOG_ERR, " netstring2msg: smax passed"); ha_msg_del(msg); return NULL; } if (!is_auth_netstring(s, slen, authstring,authlen) ){ if (!cl_msg_quiet_fmterr) { cl_log(LOG_ERR , "netstring authentication" " failed, s=%s, autotoken=%s" , s, authstring); cl_log_message(LOG_ERR, msg); } ha_msg_del(msg); return(NULL); } out: return msg; } int is_auth_netstring(const char * datap, size_t datalen, const char * authstring, size_t authlen) { char authstr[MAXLINE]; /* A copy of authstring */ int authwhich; char authtoken[MAXLINE]; /* * If we don't have any authentication method - everything is authentic... */ if (!authmethod) { return TRUE; } strncpy(authstr, authstring, MAXLINE); authstr[authlen] = 0; if (sscanf(authstr, "%d %s", &authwhich, authtoken) != 2) { if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING , "Bad/invalid netstring auth string"); } return(0); } memset(authstr, 0, MAXLINE); if (authmethod(authwhich, datap, datalen, authstr, MAXLINE) != authwhich) { if (!cl_msg_quiet_fmterr) { cl_log(LOG_WARNING , "Invalid authentication [%d] in message!" , authwhich); } return(FALSE); } if (strcmp(authtoken, authstr) == 0) { return(TRUE); } if (!cl_msg_quiet_fmterr) { cl_log(LOG_ERR , "authtoken does not match, authtoken=%s, authstr=%s" , authtoken, authstr); } return(FALSE); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_pidfile.c0000644000000000000000000001611412120057602026424 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* * The following information is from the Filesystem Hierarchy Standard * version 2.1 dated 12 April, 2000. * * 5.6 /var/lock : Lock files * Lock files should be stored within the /var/lock directory structure. * Device lock files, such as the serial device lock files that were originally * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in * /var/lock. The naming convention which must be used is LCK.. followed by * the base name of the device file. For example, to lock /dev/cua0 the file * LCK..cua0 would be created. * * The format used for device lock files must be the HDB UUCP lock file format. * The HDB format is to store the process identifier (PID) as a ten byte * ASCII decimal number, with a trailing newline. For example, if process 1230 * holds a lock file, it would contain the eleven characters: space, space, * space, space, space, space, one, two, three, zero, and newline. * Then, anything wishing to use /dev/cua0 can read the lock file and act * accordingly (all locks in /var/lock should be world-readable). * * * PERMISSIONS NOTE: * Different linux distributions set the mode of the lock directory differently * Any process which wants to create lock files must have write permissions * on FILE_LOCK_D (probably /var/lock). For things like the heartbeat API * code, this may mean allowing the uid of the processes that use this API * to join group uucp, or making the binaries setgid to uucp. */ /* The code in this file originally written by Guenther Thomsen */ /* Somewhat mangled by Alan Robertson */ /* * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL) * serial_device has to be _the complete path_, i.e. including '/dev/' to the * special file, which denotes the tty to lock -tho * return 0 on success, * -1 if device is locked (lockfile exists and isn't stale), * -2 for temporarily failure, try again, * other negative value, if something unexpected happend (failure anyway) */ /* This is what the FHS standard specifies for the size of our lock file */ #define LOCKSTRLEN 11 #include static int IsRunning(long pid) { int rc = 0; long mypid; int running = 0; char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX]; /* check if pid is running */ if (CL_KILL(pid, 0) < 0 && errno == ESRCH) { goto bail; } #ifndef HAVE_PROC_PID return 1; #endif /* check to make sure pid hasn't been reused by another process */ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid); rc = readlink(proc_path, exe_path, PATH_MAX-1); if(rc < 0) { cl_perror("Could not read from %s", proc_path); goto bail; } exe_path[rc] = 0; mypid = (unsigned long) getpid(); snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", mypid); rc = readlink(proc_path, myexe_path, PATH_MAX-1); if(rc < 0) { cl_perror("Could not read from %s", proc_path); goto bail; } myexe_path[rc] = 0; if(strcmp(exe_path, myexe_path) == 0) { running = 1; } bail: return running; } static int DoLock(const char *filename) { char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1]; int fd; long pid, mypid; int rc; struct stat sbuf; mypid = (unsigned long) getpid(); snprintf(lf_name, sizeof(lf_name), "%s",filename); snprintf(tf_name, sizeof(tf_name), "%s.%lu", filename, mypid); if ((fd = open(lf_name, O_RDONLY)) >= 0) { if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) { sleep(1); /* if someone was about to create one, * give'm a sec to do so * Though if they follow our protocol, * this won't happen. They should really * put the pid in, then link, not the * other way around. */ } if (read(fd, buf, sizeof(buf)) < 1) { /* lockfile empty -> rm it and go on */; } else { if (sscanf(buf, "%lu", &pid) < 1) { /* lockfile screwed up -> rm it and go on */ } else { if (pid > 1 && (getpid() != pid) && IsRunning(pid)) { /* is locked by existing process * -> give up */ close(fd); return -1; } else { /* stale lockfile -> rm it and go on */ } } } unlink(lf_name); close(fd); } if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) { /* Hmmh, why did we fail? Anyway, nothing we can do about it */ return -3; } /* Slight overkill with the %*d format ;-) */ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid); if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) { /* Again, nothing we can do about this */ rc = -3; close(fd); goto out; } close(fd); switch (link(tf_name, lf_name)) { case 0: if (stat(tf_name, &sbuf) < 0) { /* something weird happened */ rc = -3; break; } if (sbuf.st_nlink < 2) { /* somehow, it didn't get through - NFS trouble? */ rc = -2; break; } rc = 0; break; case EEXIST: rc = -1; break; default: rc = -3; } out: unlink(tf_name); return rc; } static int DoUnlock(const char * filename) { char lf_name[256]; snprintf(lf_name, sizeof(lf_name), "%s", filename); return unlink(lf_name); } int cl_read_pidfile(const char*filename) { long pid = 0; pid = cl_read_pidfile_no_checking(filename); if (pid < 0){ return - LSB_STATUS_STOPPED; } if (IsRunning(pid)){ return pid; }else{ return -LSB_STATUS_VAR_PID; } } int cl_read_pidfile_no_checking(const char*filename) { int fd; long pid = 0; char buf[LOCKSTRLEN+1]; if ((fd = open(filename, O_RDONLY)) < 0) { return -1; } if (read(fd, buf, sizeof(buf)) < 1) { close(fd); return -1; } if (sscanf(buf, "%lu", &pid) <= 0) { close(fd); return -1; } if (pid <= 0){ close(fd); return -1; } close(fd); return pid; } int cl_lock_pidfile(const char *filename) { if (filename == NULL) { errno = EFAULT; return -3; } return DoLock(filename); } /* * Unlock a file (remove its lockfile) * do we need to check, if its (still) ours? No, IMHO, if someone else * locked our line, it's his fault -tho * returns 0 on success * <0 if some failure occured */ int cl_unlock_pidfile(const char *filename) { if (filename == NULL) { errno = EFAULT; return -3; } return(DoUnlock(filename)); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_plugin.c0000644000000000000000000000653512120057602026314 0ustar00usergroup00000000000000 /* * cl_plugin.c: This file handle plugin loading and deleting * * Copyright (C) 2005 Guochun Shi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ /* #include */ #include #define MAXTYPES 16 #define MAXTYPELEN 64 static GHashTable* funcstable[MAXTYPES]; static PILPluginUniv* plugin_univ = NULL; static PILGenericIfMgmtRqst reqs[] = { {"compress", &funcstable[0], NULL, NULL, NULL}, {"HBcoms", &funcstable[1], NULL, NULL, NULL}, {"HBauth", &funcstable[2], NULL, NULL, NULL}, {"RAExec", &funcstable[3], NULL, NULL, NULL}, {"quorum", &funcstable[4], NULL, NULL, NULL}, {"tiebreaker", &funcstable[5], NULL, NULL, NULL}, {"quorumd", &funcstable[6], NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; static int init_pluginsys(void){ if (plugin_univ) { return TRUE; } plugin_univ = NewPILPluginUniv(HA_PLUGIN_DIR); if (plugin_univ) { if (PILLoadPlugin(plugin_univ, PI_IFMANAGER, "generic", reqs) != PIL_OK){ cl_log(LOG_ERR, "generic plugin load failed\n"); DelPILPluginUniv(plugin_univ); plugin_univ = NULL; } }else{ cl_log(LOG_ERR, "pi univ creation failed\n"); } return plugin_univ != NULL; } int cl_remove_plugin(const char* type, const char* pluginname) { return HA_OK; } void* cl_load_plugin(const char* type, const char* pluginname) { void* funcs = NULL; int i = 0; GHashTable** table = NULL; while (reqs[i].iftype != NULL){ if ( strcmp(reqs[i].iftype,type) != 0){ i++; continue; } table = reqs[i].ifmap; break; } if (table == NULL){ cl_log(LOG_ERR, "%s: function table not found",__FUNCTION__); return NULL; } if (!init_pluginsys()){ cl_log(LOG_ERR, "%s: init plugin universe failed", __FUNCTION__); return NULL; } if ((funcs = g_hash_table_lookup(*table, pluginname)) == NULL){ if (PILPluginExists(plugin_univ, type, pluginname) == PIL_OK){ PIL_rc rc; rc = PILLoadPlugin(plugin_univ, type, pluginname, NULL); if (rc != PIL_OK){ cl_log(LOG_ERR, "Cannot load plugin %s[%s]", pluginname, PIL_strerror(rc)); return NULL; } funcs = g_hash_table_lookup(*table, pluginname); } } if (funcs == NULL){ cl_log(LOG_ERR, "%s: module(%s) not found", __FUNCTION__, pluginname); return NULL; } return funcs; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_poll.c0000644000000000000000000004576412120057602025773 0ustar00usergroup00000000000000#include #include #include /* * Substitute poll(2) function using POSIX real time signals. * * The poll(2) system call often has significant latencies and realtime * impacts (probably because of its variable length argument list). * * These functions let us use real time signals and sigtimedwait(2) instead * of poll - for those files which work with real time signals. * In the 2.4 series of Linux kernels, this does *not* include FIFOs. * * NOTE: We (have to) grab the SIGPOLL signal for our own purposes. * Hope that's OK with you... * * Special caution: We can only incompletely simulate the difference between * the level-triggered interface of poll(2) and the edge-triggered behavior * of I/O signals. As a result you *must* read all previously-indicated * incoming data before calling cl_poll() again. Otherwise you may miss * some incoming data (and possibly hang). * * * Copyright (C) 2003 IBM Corporation * * Author: * * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ #define __USE_GNU 1 # include #undef __USE_GNU #include #include #include #include #include #include /* Turn on to log odd realtime behavior */ #define TIME_CALLS 1 #ifdef TIME_CALLS # include # include #endif static int debug = 0; int /* Slightly sleazy... */ cl_glibpoll(GPollFD* ufds, guint nfsd, gint timeout) { (void)debug; return cl_poll((struct pollfd*)ufds, nfsd, timeout); } #if defined (F_SETSIG) && defined(F_SETOWN) && defined (O_ASYNC) # define HAVE_FCNTL_F_SETSIG #endif #ifndef HAVE_FCNTL_F_SETSIG /* * Dummy cl_poll() and cl_poll_ignore() functions for systems where * we don't have all the support we need. */ int cl_poll(struct pollfd *fds, unsigned int nfds, int timeout) { return poll(fds, (nfds_t)nfds, timeout); } int cl_poll_ignore(int fd) { return 0; } #else /* HAVE_FCNTL_F_SETSIG */ static void dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms); static void check_fd_info(struct pollfd *fds, unsigned int nfds); static void cl_real_poll_fd(int fd); static void cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* , void*); static void cl_poll_sigpoll_overflow(void); static int cl_poll_get_sigqlimit(void); typedef unsigned char poll_bool; /* * Here's our strategy: * We have a set of signals which we use for these file descriptors, * and we use sigtimedwait(2) to wait on information from these various * signals. * * If we are ever asked to wait for a particular signal, then we will * enable signals for that file descriptor, and post the events in * our own cache. The next time you include that signal in a call * to cl_poll(), you will get the information delivered * to you in your cl_poll() call. * * If you want to stop monitoring a particular file descriptor, use * cl_poll_ignore() for that purpose. Doing this is a good idea, but * not fatal if omitted... */ /* Information about a file descriptor we're monitoring */ typedef struct poll_fd_info_s { short nsig; /* Which signal goes with it? */ short pendevents; /* Pending events */ }poll_info_t; static int max_allocated = 0; static poll_bool* is_monitored = NULL; /* Sized by max_allocated */ static poll_info_t* monitorinfo = NULL; /* Sized by max_allocated */ static int cl_nsig = 0; static gboolean SigQOverflow = FALSE; static int cl_init_poll_sig(struct pollfd *fds, unsigned int nfds); static short cl_poll_assignsig(int fd); static void cl_poll_sigaction(int nsig, siginfo_t* info, void* v); static int cl_poll_prepsig(int nsig); /* * SignalSet is the set of all file descriptors we're monitoring. * * We monitor a file descriptor forever, unless you tell us not to * by calling cl_poll_ignore(), or you (mistakenly) give it to * us to look at in another poll call after you've closed it. */ static sigset_t SignalSet; /* Select the signal you want us to use (must be a RT signal) */ int cl_poll_setsig(int nsig) { if (nsig < SIGRTMIN || nsig >= SIGRTMAX) { errno = EINVAL; return -1; } if (cl_poll_prepsig(nsig) < 0) { return -1; } cl_nsig = nsig; return 0; } /* * It's harmless to call us multiple times on the same signal. */ static int cl_poll_prepsig(int nsig) { static gboolean setinityet=FALSE; if (!setinityet) { CL_SIGEMPTYSET(&SignalSet); cl_signal_set_simple_action(SIGPOLL , cl_poll_sigpoll_overflow_sigaction , NULL); setinityet = TRUE; } if (CL_SIGINTERRUPT(nsig, FALSE) < 0) { cl_perror("sig_interrupt(%d, FALSE)", nsig); return -1; } if (CL_SIGADDSET(&SignalSet, nsig) < 0) { cl_perror("sig_addset(&SignalSet, %d)", nsig); return -1; } if (CL_SIGPROCMASK(SIG_BLOCK, &SignalSet, NULL) < 0) { cl_perror("sig_sigprocmask(SIG_BLOCK, sig %d)", nsig); return -1; } if (debug) { cl_log(LOG_DEBUG , "Signal %d belongs to us...", nsig); cl_log(LOG_DEBUG, "cl_poll_prepsig(%d) succeeded.", nsig); } return 0; } #define FD_CHUNKSIZE 64 /* Set of events everyone must monitor whether they want to or not ;-) */ #define CONSTEVENTS (POLLHUP|POLLERR|POLLNVAL) #define RECORDFDEVENT(fd, flags) (monitorinfo[fd].pendevents |= (flags)) /* * Initialized our poll-simulation data structures. * This means (among other things) registering any monitored * file descriptors. */ static int cl_init_poll_sig(struct pollfd *fds, unsigned int nfds) { unsigned j; int maxmonfd = -1; int nmatch = 0; if (cl_nsig == 0) { cl_nsig = ((SIGRTMIN+SIGRTMAX)/2); if (cl_poll_setsig(cl_nsig) < 0) { return -1; } } for (j=0; j < nfds; ++j) { const int fd = fds[j].fd; if (fd > maxmonfd) { maxmonfd = fd; } } /* See if we need to malloc/realloc our data structures */ if (maxmonfd >= max_allocated) { int newsize; int growthamount; newsize = ((maxmonfd + FD_CHUNKSIZE)/FD_CHUNKSIZE) * FD_CHUNKSIZE; growthamount = newsize - max_allocated; /* This can't happen ;-) */ if (growthamount <= 0 || newsize <= maxmonfd) { errno = EINVAL; return -1; } /* Allocate (more) memory! */ if ((is_monitored = (poll_bool*)realloc(is_monitored , newsize * sizeof(poll_bool))) == NULL || (monitorinfo = (poll_info_t*) realloc(monitorinfo , newsize * sizeof(poll_info_t))) == NULL) { if (is_monitored) { free(is_monitored); is_monitored = NULL; } if (monitorinfo) { free(monitorinfo); monitorinfo = NULL; } max_allocated = 0; errno = ENOMEM; return -1; } memset(monitorinfo+max_allocated, 0 , growthamount * sizeof(monitorinfo[0])); memset(is_monitored+max_allocated, FALSE , growthamount*sizeof(is_monitored[0])); max_allocated = newsize; } if (fds->events != 0 && debug) { cl_log(LOG_DEBUG , "Current event mask for fd [0] {%d} 0x%x" , fds->fd, fds->events); } /* * Examine each fd for the following things: * Is it already monitored? * if not, set it up for monitoring. * Do we have events for it? * if so, post events... */ for (j=0; j < nfds; ++j) { const int fd = fds[j].fd; poll_info_t* moni = monitorinfo+fd; short nsig; int badfd = FALSE; is_monitored[fd] = TRUE; if (moni->nsig <= 0) { nsig = cl_poll_assignsig(fd); if (nsig < 0) { RECORDFDEVENT(fd, POLLERR); badfd = TRUE; }else{ /* Use real poll(2) to get initial * event status */ moni->nsig = nsig; cl_real_poll_fd(fd); } }else if (fcntl(fd, F_GETFD) < 0) { cl_log(LOG_ERR, "bad fd(%d)", fd); RECORDFDEVENT(fd, POLLNVAL); badfd = TRUE; } /* Look for pending events... */ fds[j].revents = (moni->pendevents & (fds[j].events|CONSTEVENTS)); if (fds[j].revents) { ++nmatch; moni->pendevents &= ~(fds[j].revents); if (debug) { cl_log(LOG_DEBUG , "revents for fd %d: 0x%x" , fds[j].fd, fds[j].revents); cl_log(LOG_DEBUG , "events for fd %d: 0x%x" , fds[j].fd, fds[j].events); } }else if (fds[j].events && debug) { cl_log(LOG_DEBUG , "pendevents for fd %d: 0x%x" , fds[j].fd, moni->pendevents); } if (badfd) { cl_poll_ignore(fd); } } if (nmatch != 0 && debug) { cl_log(LOG_DEBUG, "Returning %d events from cl_init_poll_sig()" , nmatch); } return nmatch; } /* * Initialize our current state of the world with info from the * real poll(2) call. * * We call this when we first see a particular fd, and after a signal * queue overflow. */ static void cl_real_poll_fd(int fd) { struct pollfd pfd[1]; if (fd >= max_allocated || !is_monitored[fd]) { return; } if (debug) { cl_log(LOG_DEBUG , "Calling poll(2) on fd %d", fd); } /* Get the current state of affaris from poll(2) */ pfd[0].fd = fd; pfd[0].revents = 0; pfd[0].events = ~0; if (poll(pfd, 1, 0) >= 0) { RECORDFDEVENT(fd, pfd[0].revents); if (pfd[0].revents & (POLLNVAL|POLLERR)) { cl_log(LOG_INFO, "cl_poll_real_fd(%d): error in revents [%d]" , fd, pfd[0].revents); } if (debug) { cl_log(LOG_DEBUG , "Old news from poll(2) for fd %d: 0x%x" , fd, pfd[0].revents); } }else{ if (fcntl(fd, F_GETFL) < 0) { cl_perror("cl_poll_real_fd(%d): F_GETFL failure" , fd); RECORDFDEVENT(fd, POLLNVAL); }else{ RECORDFDEVENT(fd, POLLERR); } } } /* * Assign a signal for monitoring the given file descriptor */ static short cl_poll_assignsig(int fd) { int flags; if (debug) { cl_log(LOG_DEBUG , "Signal %d monitors fd %d...", cl_nsig, fd); } /* Test to see if the file descriptor is good */ if ((flags = fcntl(fd, F_GETFL)) < 0) { cl_perror("cl_poll_assignsig(%d) F_GETFL failure" , fd); return -1; } /* Associate the right signal with the fd */ if (fcntl(fd, F_SETSIG, cl_nsig) < 0) { cl_perror("cl_poll_assignsig(%d) F_SETSIG failure" , fd); return -1; } /* Direct the signals to us */ if (fcntl(fd, F_SETOWN, getpid()) < 0) { cl_perror("cl_poll_assignsig(%d) F_SETOWN failure", fd); return -1; } /* OK... Go ahead and send us signals! */ if (fcntl(fd, F_SETFL, flags|O_ASYNC) < 0) { cl_perror("cl_poll_assignsig(%d) F_SETFL(O_ASYNC) failure" , fd); return -1; } return cl_nsig; } /* * This is a function we call as a (fake) signal handler. * * It records events to our "monitorinfo" structure. * * Except for the cl_log() call, it could be called in a signal * context. */ static void cl_poll_sigaction(int nsig, siginfo_t* info, void* v) { int fd; /* What do you suppose all the various si_code values mean? */ fd = info->si_fd; if (debug) { cl_log(LOG_DEBUG , "cl_poll_sigaction(nsig=%d fd=%d" ", si_code=%d si_band=0x%lx)" , nsig, fd, info->si_code , (unsigned long)info->si_band); } if (fd <= 0) { return; } if (fd >= max_allocated || !is_monitored[fd]) { return; } /* We should not call logging functions in (real) signal handlers */ if (nsig != monitorinfo[fd].nsig) { cl_log(LOG_ERR, "cl_poll_sigaction called with signal %d/%d" , nsig, monitorinfo[fd].nsig); } /* Record everything as a pending event. */ RECORDFDEVENT(fd, info->si_band); } /* * This is called whenever a file descriptor shouldn't be * monitored any more. */ int cl_poll_ignore(int fd) { int flags; if (debug) { cl_log(LOG_DEBUG , "cl_poll_ignore(%d)", fd); } if (fd < 0 || fd >= max_allocated) { errno = EINVAL; return -1; } if (!is_monitored[fd]) { return 0; } is_monitored[fd] = FALSE; memset(monitorinfo+fd, 0, sizeof(monitorinfo[0])); if ((flags = fcntl(fd, F_GETFL)) >= 0) { flags &= ~O_ASYNC; if (fcntl(fd, F_SETFL, flags) < 0) { return -1; } }else{ return flags; } return 0; } /* * cl_poll: fake poll routine based on POSIX realtime signals. * * We want to emulate poll as exactly as possible, but poll has a couple * of problems: scaleability, and it tends to sleep in the kernel * because the first argument is an argument of arbitrary size, and * generally requires allocating memory. * * The challenge is that poll is level-triggered, but the POSIX * signals (and sigtimedwait(2)) are edge triggered. This is * one of the reasons why we have the cl_real_poll_fd() function * - to get the current "level" before we start. * Once we have this level we can compute something like the current * level */ int cl_poll(struct pollfd *fds, unsigned int nfds, int timeoutms) { int nready; struct timespec ts; static const struct timespec zerotime = {0L, 0L}; const struct timespec* itertime = &ts; siginfo_t info; int eventcount = 0; unsigned int j; int savederrno = errno; int stw_errno; int rc; longclock_t starttime; longclock_t endtime; const int msfudge = 2* 1000/hz_longclock(); int mselapsed = 0; /* Do we have any old news to report? */ if ((nready=cl_init_poll_sig(fds, nfds)) != 0) { /* Return error or old news to report */ if (debug) { cl_log(LOG_DEBUG, "cl_poll: early return(%d)", nready); } return nready; } /* Nothing to report yet... */ /* So, we'll do a sigtimedwait(2) to wait for signals * and see if we can find something to report... * * cl_init_poll() prepared a set of file signals to watch... */ recalcandwaitagain: if (timeoutms >= 0) { ts.tv_sec = timeoutms / 1000; ts.tv_nsec = (((unsigned long)timeoutms) % 1000UL)*1000000UL; }else{ ts.tv_sec = G_MAXLONG; ts.tv_nsec = 99999999UL; } /* * Perform a timed wait for any of our signals... * * We shouldn't sleep for any call but (possibly) the first one. * Subsequent calls should just pick up other events without * sleeping. */ starttime = time_longclock(); /* * Wait up to the prescribed time for a signal. * If we get a signal, then loop grabbing all other * pending signals. Note that subsequent iterations will * use &zerotime to get the minimum wait time. */ if (debug) { check_fd_info(fds, nfds); dump_fd_info(fds, nfds, timeoutms); } waitagain: while (sigtimedwait(&SignalSet, &info, itertime) >= 0) { int nsig = info.si_signo; /* Call signal handler to simulate signal reception */ cl_poll_sigaction(nsig, &info, NULL); itertime = &zerotime; } stw_errno=errno; /* Save errno for later use */ endtime = time_longclock(); mselapsed = longclockto_ms(sub_longclock(endtime, starttime)); #ifdef TIME_CALLS if (timeoutms >= 0 && mselapsed > timeoutms + msfudge) { /* We slept too long... */ cl_log(LOG_WARNING , "sigtimedwait() sequence for %d ms took %d ms" , timeoutms, mselapsed); } #endif if (SigQOverflow) { /* OOPS! Better recover from this! */ /* This will use poll(2) to correct our current status */ cl_poll_sigpoll_overflow(); } /* Post observed events and count them... */ for (j=0; j < nfds; ++j) { int fd = fds[j].fd; poll_info_t* moni = monitorinfo+fd; fds[j].revents = (moni->pendevents & (fds[j].events|CONSTEVENTS)); if (fds[j].revents) { ++eventcount; moni->pendevents &= ~(fds[j].revents); /* Make POLLHUP persistent */ if (fds[j].revents & POLLHUP) { moni->pendevents |= POLLHUP; /* Don't lose input events at EOF */ if (fds[j].events & POLLIN) { cl_real_poll_fd(fds[j].fd); } } } } if (eventcount == 0 && stw_errno == EAGAIN && timeoutms != 0) { /* We probably saw an event the user didn't ask to see. */ /* Consquently, we may have more waiting to do */ if (timeoutms < 0) { /* Restore our infinite wait time */ itertime = &ts; goto waitagain; }else if (timeoutms > 0) { if (mselapsed < timeoutms) { timeoutms -= mselapsed; goto recalcandwaitagain; } } } rc = (eventcount > 0 ? eventcount : (stw_errno == EAGAIN ? 0 : -1)); if (rc >= 0) { errno = savederrno; } return rc; } /* * Debugging routine for printing current poll arguments, etc. */ static void dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms) { unsigned j; cl_log(LOG_DEBUG, "timeout: %d milliseconds", timeoutms); for (j=0; j < nfds; ++j) { int fd = fds[j].fd; poll_info_t* moni = monitorinfo+fd; cl_log(LOG_DEBUG, "fd %d flags: 0%o, signal: %d, events: 0x%x" ", revents: 0x%x, pendevents: 0x%x" , fd, fcntl(fd, F_GETFL), moni->nsig , fds[j].events, fds[j].revents, moni->pendevents); } for (j=SIGRTMIN; j < (unsigned)SIGRTMAX; ++j) { if (!sigismember(&SignalSet, j)) { continue; } cl_log(LOG_DEBUG, "Currently monitoring RT signal %d", j); } } /* * Debugging routine for auditing our file descriptors, etc. */ static void check_fd_info(struct pollfd *fds, unsigned int nfds) { unsigned j; for (j=0; j < nfds; ++j) { int fd = fds[j].fd; poll_info_t* moni = monitorinfo+fd; if (!sigismember(&SignalSet, moni->nsig)) { cl_log(LOG_ERR, "SIGNAL %d not in monitored SignalSet" , moni->nsig); } } for (j=0; j < 10; ++j) { int sig; int flags; int pid; if ((flags = fcntl(j, F_GETFL)) < 0 || (flags & O_ASYNC) ==0){ continue; } sig = fcntl(j, F_GETSIG); if (sig == 0) { cl_log(LOG_ERR, "FD %d will get SIGIO", j); } if (!sigismember(&SignalSet, sig)) { cl_log(LOG_ERR, "FD %d (signal %d) is not in SignalSet" , j, sig); } if (sig < SIGRTMIN || sig >= SIGRTMAX) { cl_log(LOG_ERR, "FD %d (signal %d) is not RealTime" , j, sig); } pid = fcntl(j, F_GETOWN); if (pid != getpid()) { cl_log(LOG_ERR, "FD %d (signal %d) owner is pid %d" , j, sig, pid); } } } /* Note that the kernel signalled an event queue overflow */ static void cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* info, void* v) { SigQOverflow = TRUE; } #define MAXQNAME "rtsig-max" /* * Called when signal queue overflow is suspected. * We then use poll(2) to get the current data. It's slow, but it * should work quite nicely. */ static void cl_poll_sigpoll_overflow(void) { int fd; int limit; if (!SigQOverflow) { return; } cl_log(LOG_WARNING, "System signal queue overflow."); limit = cl_poll_get_sigqlimit(); if (limit > 0) { cl_log(LOG_WARNING, "Increase '%s'. Current limit is %d" " (see sysctl(8)).", MAXQNAME, limit); } SigQOverflow = FALSE; for (fd = 0; fd < max_allocated; ++fd) { if (is_monitored[fd]) { cl_real_poll_fd(fd); } } } #define PSK "/proc/sys/kernel/" /* Get current kernel signal queue limit */ /* This only works on Linux - but that's not a big problem... */ static int cl_poll_get_sigqlimit(void) { int limit = -1; int pfd; char result[32]; pfd = open(PSK MAXQNAME, O_RDONLY); if (pfd >= 0 && read(pfd, result, sizeof(result)) > 1) { limit = atoi(result); if (limit < 1) { limit = -1; } } if (pfd >= 0) { close(pfd); } return limit; } #endif /* HAVE_FCNTL_F_SETSIG */ Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_random.c0000644000000000000000000001115012120057602026263 0ustar00usergroup00000000000000/* * Copyright (C) 2005 Guochun Shi * Copyright (C) 2005 International Business Machines Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_TIME_H #include #endif #include #include /* Used to provide seed to the random number generator */ unsigned int cl_randseed(void) { char buf[16]; FILE* fs; struct timeval tv; const char * randdevname [] = {"/dev/urandom", "/dev/random"}; int idev; #if 0 long horrid; #endif /* * Notes, based on reading of man pages of Solaris, FreeBSD and Linux, * and on proof-of-concept tests on Solaris and Linux (32- and 64-bit). * * Reminder of a subtlety: our intention is not to return a random * number, but rather to return a random-enough seed for future * random numbers. So don't bother trying (e.g.) "rand()" and * "random()". * * /dev/random and dev/urandom seem to be a related pair. In the * words of the song: "You can't have one without the other". * * /dev/random is probably the best. But it can block. The Solaris * implementation can apparently honour "O_NONBLOCK" and "O_NDELAY". * But can others? For this reason, I chose not to use it at present. * * /dev/urandom (with the "u") is also good. This doesn't block. * But some OSes may lack it. It is tempting to detect its presence * with autoconf and use the result in a "hash-if" here. BUT... in * at least one OS, its presence can vary depending upon patch levels, * so a binary/package built on an enabled machine might hit trouble * when run on one where it is absent. (And vice versa: a build on a * disabled machine would be unable to take advantage of it on an * enabled machine.) Therefore always try for it at run time. * * "gettimeofday()" returns a random-ish number in its millisecond * component. * * -- David Lee, Jan 2006 */ /* * Each block below is logically of the form: * if good-feature appears present { * try feature * if feature worked { * return its result * } * } * -- fall through to not-quite-so-good feature -- */ /* * Does any of the random device names work? */ for (idev=0; idev < DIMOF(randdevname); ++idev) { fs = fopen(randdevname[idev], "r"); if (fs == NULL) { cl_log(LOG_INFO, "%s: Opening file %s failed" , __FUNCTION__, randdevname[idev]); }else{ if (fread(buf, 1, sizeof(buf), fs)!= sizeof(buf)){ cl_log(LOG_INFO, "%s: reading file %s failed" , __FUNCTION__, randdevname[idev]); fclose(fs); }else{ fclose(fs); return (unsigned int)cl_binary_to_int(buf, sizeof(buf)); } } } /* * Try "gettimeofday()"; use its microsecond output. * (Might it be prudent to let, say, the seconds further adjust this, * in case the microseconds are too predictable?) */ if (gettimeofday(&tv, NULL) != 0) { cl_log(LOG_INFO, "%s: gettimeofday failed", __FUNCTION__); }else{ return (unsigned int) tv.tv_usec; } /* * times(2) returns the number of clock ticks since * boot. Fairly predictable, but not completely so... */ return (unsigned int) cl_times(); #if 0 /* * If all else has failed, return (as a number) the address of * something on the stack. * Poor, but at least it has a chance of some sort of variability. */ horrid = (long) &tv; return (unsigned int) horrid; /* pointer to local variable exposed */ #endif } static gboolean inityet = FALSE; static void cl_init_random(void) { if (inityet) return; inityet=TRUE; srand(cl_randseed()); } int get_next_random(void) { if (!inityet) cl_init_random(); return rand(); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_reboot.c0000644000000000000000000000242012120057602026275 0ustar00usergroup00000000000000#include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_REBOOT_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #include #include enum rebootopt { REBOOT_DEFAULT = 0, REBOOT_NOCOREDUMP = 1, REBOOT_COREDUMP = 2, }; static enum rebootopt coredump = REBOOT_DEFAULT; void cl_enable_coredump_before_reboot(gboolean yesno) { coredump = (yesno ? REBOOT_COREDUMP : REBOOT_NOCOREDUMP); } void cl_reboot(int msdelaybeforereboot, const char * reason) { int rebootflag = 0; int systemrc = 0; #ifdef RB_AUTOBOOT rebootflag = RB_AUTOBOOT; #endif #ifdef RB_NOSYNC rebootflag = RB_NOSYNC; #endif #ifdef RB_DUMP if (coredump == REBOOT_COREDUMP) { rebootflag = RB_DUMP; } #endif cl_log(LOG_EMERG, "Rebooting system. Reason: %s", reason); sync(); mssleep(msdelaybeforereboot); #if REBOOT_ARGS == 1 reboot(rebootflag); #elif REBOOT_ARGS == 2 reboot(rebootflag, NULL); #else #error "reboot() call needs to take one or two args" #endif /* Shouldn't ever get here, but just in case... */ systemrc=system(REBOOT " " REBOOT_OPTIONS); cl_log(LOG_EMERG, "ALL REBOOT OPTIONS FAILED: %s returned %d" , REBOOT " " REBOOT_OPTIONS, systemrc); exit(1); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_signal.c0000644000000000000000000001031312120057602026260 0ustar00usergroup00000000000000/* * cl_signal.c: signal handling routines to be used by Linux-HA programmes * * Copyright (C) 2002 Horms * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include int cl_signal_set_handler(int sig, void (*handler)(int), sigset_t *mask , int flags, struct sigaction *oldact) { struct sigaction sa; sa.sa_handler = handler; sa.sa_mask = *mask; sa.sa_flags = flags; if (sigaction(sig, &sa, oldact) < 0) { cl_perror("cl_signal_set_handler(): sigaction()"); return(-1); } return(0); } int cl_signal_set_simple_handler(int sig, void (*handler)(int) , struct sigaction *oldact) { struct sigaction sa; sigset_t mask; if(sigemptyset(&mask) < 0) { cl_perror("cl_signal_set_simple_handler(): " "sigemptyset()"); return(-1); } sa.sa_handler = handler; sa.sa_mask = mask; sa.sa_flags = 0; if(sigaction(sig, &sa, oldact) < 0) { cl_perror("cl_signal_set_simple_handler()" ": sigaction()"); return(-1); } return(0); } int cl_signal_set_action(int sig, void (*action)(int, siginfo_t *, void *) , sigset_t *mask, int flags, struct sigaction *oldact) { struct sigaction sa; sa.sa_sigaction = action; sa.sa_mask = *mask; sa.sa_flags = flags; if(sigaction(sig, &sa, oldact) < 0) { cl_perror("cl_signal_set_action(): sigaction()"); return(-1); } return(0); } int cl_signal_set_simple_action(int sig, void (*action)(int, siginfo_t *, void *) , struct sigaction *oldact) { struct sigaction sa; sigset_t mask; if(sigemptyset(&mask) < 0) { cl_perror("cl_signal_set_simple_action()" ": sigemptyset()"); return(-1); } sa.sa_sigaction = action; sa.sa_mask = mask; sa.sa_flags = 0; if(sigaction(sig, &sa, oldact) < 0) { cl_perror("cl_signal_set_simple_action()" ": sigaction()"); return(-1); } return(0); } int cl_signal_set_interrupt(int sig, int flag) { if(siginterrupt(sig, flag) < 0) { cl_perror("cl_signal_set_interrupt(): siginterrupt()"); return(-1); } return(0); } int cl_signal_block(int how, int signal, sigset_t *oldset) { sigset_t set; if(sigemptyset(&set) < 0) { cl_perror("cl_signal_block(): sigemptyset()"); return(-1); } if(sigaddset(&set, signal) < 0) { cl_perror("cl_signal_block(): sigaddset()"); return(-1); } if(sigprocmask(how, &set, oldset) < 0) { cl_perror("cl_signal_block(): sigprocmask()"); return(-1); } return(0); } int cl_signal_block_set(int how, const sigset_t *set, sigset_t *oldset) { if(sigprocmask(how, set, oldset) < 0) { cl_perror("cl_signal_block_mask(): sigprocmask()"); return(-1); } return(0); } int cl_signal_set_handler_mode(const cl_signal_mode_t *mode, sigset_t *set) { size_t i; sigset_t our_set; sigset_t *use_set; use_set = (set) ? set : &our_set; for (i=0; mode[i].sig; ++i) { if(sigaddset(use_set, mode[i].sig) < 0) { cl_perror("cl_signal_set_handler_mode(): " "sigaddset() [signum=%d]", mode[i].sig); return(-1); } } if (sigprocmask(SIG_UNBLOCK, use_set, NULL) < 0) { cl_perror("cl_signal_set_handler_mode()" ": sigprocmask()"); return(-1); } for (i=0; mode[i].sig; ++i) { if(cl_signal_set_handler(mode[i].sig, mode[i]. handler , use_set, SA_NOCLDSTOP, NULL) < 0) { cl_log(LOG_ERR, "cl_signal_set_handler_mode(): " "ha_set_sig_handler()"); return(-1); } if(cl_signal_set_interrupt(mode[i].sig, mode[i].interrupt) < 0) { cl_log(LOG_ERR, "cl_signal_set_handler_mode(): " "hb_signal_interrupt()"); return(-1); } } return(0); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_syslog.c0000644000000000000000000000617212120057602026333 0ustar00usergroup00000000000000/* * Functions to support syslog. * David Lee (c) 2005 */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include /* * Some OSes already have tables to convert names into corresponding numbers. * For instance Linux makes these available if SYSLOG_NAMES is defined. */ #define SYSLOG_NAMES #include #include #include #include struct _syslog_code { const char *c_name; int c_val; }; #if defined(HAVE_SYSLOG_FACILITYNAMES) /* * will have included a table called "facilitynames" structured * as a "struct _syslog_code" but the tag "_syslog_code" may be something else. */ #else struct _syslog_code facilitynames[] = { #ifdef LOG_AUTH { "auth", LOG_AUTH }, { "security", LOG_AUTH }, /* DEPRECATED */ #endif #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif #ifdef LOG_CRON { "cron", LOG_CRON }, #endif #ifdef LOG_DAEMON { "daemon", LOG_DAEMON }, #endif #ifdef LOG_FTP { "ftp", LOG_FTP }, #endif #ifdef LOG_KERN { "kern", LOG_KERN }, #endif #ifdef LOG_LPR { "lpr", LOG_LPR }, #endif #ifdef LOG_MAIL { "mail", LOG_MAIL }, #endif /* { "mark", INTERNAL_MARK }, * INTERNAL */ #ifdef LOG_NEWS { "news", LOG_NEWS }, #endif #ifdef LOG_SYSLOG { "syslog", LOG_SYSLOG }, #endif #ifdef LOG_USER { "user", LOG_USER }, #endif #ifdef LOG_UUCP { "uucp", LOG_UUCP }, #endif #ifdef LOG_LOCAL0 { "local0", LOG_LOCAL0 }, #endif #ifdef LOG_LOCAL1 { "local1", LOG_LOCAL1 }, #endif #ifdef LOG_LOCAL2 { "local2", LOG_LOCAL2 }, #endif #ifdef LOG_LOCAL3 { "local3", LOG_LOCAL3 }, #endif #ifdef LOG_LOCAL4 { "local4", LOG_LOCAL4 }, #endif #ifdef LOG_LOCAL5 { "local5", LOG_LOCAL5 }, #endif #ifdef LOG_LOCAL6 { "local6", LOG_LOCAL6 }, #endif #ifdef LOG_LOCAL7 { "local7", LOG_LOCAL7 }, #endif { NULL, -1 } }; #endif /* HAVE_SYSLOG_FACILITYNAMES */ /* Convert string "auth" to equivalent number "LOG_AUTH" etc. */ int cl_syslogfac_str2int(const char *fname) { int i; if(fname == NULL || strcmp("none", fname) == 0) { return 0; } for (i = 0; facilitynames[i].c_name != NULL; i++) { if (strcmp(fname, facilitynames[i].c_name) == 0) { return facilitynames[i].c_val; } } return -1; } /* Convert number "LOG_AUTH" to equivalent string "auth" etc. */ const char * cl_syslogfac_int2str(int fnum) { int i; for (i = 0; facilitynames[i].c_name != NULL; i++) { if (facilitynames[i].c_val == fnum) { return facilitynames[i].c_name; } } return NULL; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cl_uuid.c0000644000000000000000000001015612120057602025756 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /* * uuid: wrapper declarations. * * heartbeat originally used "uuid" functionality by calling directly, * and only, onto the "e2fsprogs" implementation. * * The run-time usages in the code have since been abstracted, funnelled * through a thin, common interface layer: a Good Thing. * * Similarly, the compile-time usages of "include " are * replaced, being funnelled through a reference to this header file. * * This header file interfaces onto the actual underlying implementation. * In the case of the "e2fsprogs" implementation, it is simply a stepping * stone onto "". As other implementations are accommodated, * so their header requirements can be accommodated here. * * Copyright (C) 2004 David Lee */ #if defined (HAVE_UUID_UUID_H) /* * Almost certainly the "e2fsprogs" implementation. */ # include /* elif defined(HAVE...UUID_OTHER_1 e.g. OSSP ...) */ /* elif defined(HAVE...UUID_OTHER_2...) */ #else # include #endif #include #include #include void cl_uuid_copy(cl_uuid_t* dst, cl_uuid_t* src) { if (dst == NULL || src == NULL){ cl_log(LOG_ERR, "cl_uuid_copy: " "wrong argument %s is NULL", dst == NULL?"dst":"src"); assert(0); } uuid_copy(dst->uuid, src->uuid); } void cl_uuid_clear(cl_uuid_t* uu) { if (uu == NULL){ cl_log(LOG_ERR, "cl_uuid_clear: " "wrong argument (uu is NULL)"); assert(0); } uuid_clear(uu->uuid); } int cl_uuid_compare(const cl_uuid_t* uu1, const cl_uuid_t* uu2) { if (uu1 == NULL || uu2 == NULL){ cl_log(LOG_ERR, "cl_uuid_compare: " " wrong argument (%s is NULL)", uu1 == NULL?"uu1":"uu2"); assert(0); } return uuid_compare(uu1->uuid, uu2->uuid); } void cl_uuid_generate(cl_uuid_t* out) { if (out == NULL){ cl_log(LOG_ERR, "cl_uuid_generate: " " wrong argument (out is NULL)"); assert(0); } uuid_generate(out->uuid); } int cl_uuid_is_null(cl_uuid_t* uu) { if (uu == NULL){ cl_log(LOG_ERR, "cl_uuid_is_null: " "wrong argument (uu is NULL)"); assert(0); } return uuid_is_null(uu->uuid); } int cl_uuid_parse( char *in, cl_uuid_t* uu) { if (in == NULL || uu == NULL){ cl_log(LOG_ERR, "cl_uuid_parse: " "wrong argument (%s is NULL)", in == NULL? "in":"uu"); assert(0); } return uuid_parse(in, uu->uuid); } void cl_uuid_unparse(const cl_uuid_t* uu, char *out){ if (uu == NULL || out == NULL){ cl_log(LOG_ERR, "cl_uuid_unparse: " "wrong argument (%s is NULL)", uu == NULL? "uu":"out"); assert(0); } uuid_unparse(uu->uuid, out); } guint cl_uuid_g_hash(gconstpointer uuid_ptr) { guint ret = 0U; guint32 value32; int index; const unsigned char * uuid_char = uuid_ptr; /* It is probably not strictly necessary, but I'm trying to get the * same hash result on all platforms. After all, the uuids are the * same on every platform. */ for (index = 0; index < sizeof(cl_uuid_t); index += sizeof(value32)) { memcpy(&value32, uuid_char+index, sizeof (value32)); ret += g_ntohl(value32); } return ret; } gboolean cl_uuid_g_equal(gconstpointer uuid_ptr_a, gconstpointer uuid_ptr_b) { return cl_uuid_compare(uuid_ptr_a, uuid_ptr_b) == 0; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/coredumps.c0000644000000000000000000001745312120057602026342 0ustar00usergroup00000000000000/* * Basic Core dump control functions. * * Author: Alan Robertson * * Copyright (C) 2004 IBM Corporation * * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_PRCTL_H # include #endif #include #include #include #include static char * coreroot = NULL; /* Set the root directory of our core directory hierarchy */ int cl_set_corerootdir(const char * dir) { if (dir == NULL || *dir != '/') { cl_perror("Invalid dir in cl_set_corerootdir() [%s]" , dir ? dir : ""); errno = EINVAL; return -1; } if (coreroot != NULL) { free(coreroot); coreroot = NULL; } coreroot = strdup(dir); if (coreroot == NULL) { return -1; } return 0; } /* * Change directory to the directory our core file needs to go in * Call after you establish the userid you're running under. */ int cl_cdtocoredir(void) { const char * dir = coreroot; int rc; struct passwd* pwent; if (dir == NULL) { dir = HA_COREDIR; } if ((rc=chdir(dir)) < 0) { int errsave = errno; cl_perror("Cannot chdir to [%s]", dir); errno = errsave; return rc; } pwent = getpwuid(getuid()); if (pwent == NULL) { int errsave = errno; cl_perror("Cannot get name for uid [%d]", getuid()); errno = errsave; return -1; } if ((rc=chdir(pwent->pw_name)) < 0) { int errsave = errno; cl_perror("Cannot chdir to [%s/%s]", dir, pwent->pw_name); errno = errsave; } return rc; } #define CHECKED_KERNEL_CORE_ENV "_PROC_SYS_CORE_CHECKED_" #define PROC_SYS_KERNEL_CORE_PID "/proc/sys/kernel/core_uses_pid" #define PROC_SYS_KERNEL_CORE_PAT "/proc/sys/kernel/core_pattern" static void cl_coredump_signal_handler(int nsig); /* * core_uses_pid(): * * returns {-1, 0, 1} * -1: not supported * 0: supported and disabled * 1: supported and enabled */ #define BUF_MAX 256 static int core_uses_pid(void) { const char * uses_pid_pathnames[] = {PROC_SYS_KERNEL_CORE_PID}; const char * corepats_pathnames[] = {PROC_SYS_KERNEL_CORE_PAT}; const char * goodpats [] = {"%t", "%p"}; int j; for (j=0; j < DIMOF(corepats_pathnames); ++j) { int fd; char buf[BUF_MAX]; int rc; int k; if ((fd = open(corepats_pathnames[j], O_RDONLY)) < 0) { continue; } memset(buf, 0, BUF_MAX); rc = read(fd, buf, BUF_MAX - 1); /* Ensure it is always NULL terminated */ close(fd); for (k=0; rc > 0 && k < DIMOF(goodpats); ++k) { if (strstr(buf, goodpats[k]) != NULL) { return 1; } } break; } for (j=0; j < DIMOF(uses_pid_pathnames); ++j) { int fd; char buf[2]; int rc; if ((fd = open(uses_pid_pathnames[j], O_RDONLY)) < 0) { continue; } rc = read(fd, buf, sizeof(buf)); close(fd); if (rc < 1) { continue; } return (buf[0] == '1'); } setenv(CHECKED_KERNEL_CORE_ENV, "1", TRUE); return -1; } /* Enable/disable core dumps for ourselves and our child processes */ int cl_enable_coredumps(int doenable) { int rc; struct rlimit rlim; if ((rc = getrlimit(RLIMIT_CORE, &rlim)) < 0) { int errsave = errno; cl_perror("Cannot get current core limit value."); errno = errsave; return rc; } if (rlim.rlim_max == 0 && geteuid() == 0) { rlim.rlim_max = RLIM_INFINITY; } rlim.rlim_cur = (doenable ? rlim.rlim_max : 0); if (doenable && rlim.rlim_max == 0) { cl_log(LOG_WARNING , "Not possible to enable core dumps (rlim_max is 0)"); } if ((rc = setrlimit(RLIMIT_CORE, &rlim)) < 0) { int errsave = errno; cl_perror("Unable to %s core dumps" , doenable ? "enable" : "disable"); errno = errsave; return rc; } if (getenv(CHECKED_KERNEL_CORE_ENV) == NULL && core_uses_pid() == 0) { cl_log(LOG_WARNING , "Core dumps could be lost if multiple dumps occur."); cl_log(LOG_WARNING , "Consider setting non-default value in %s" " (or equivalent) for maximum supportability", PROC_SYS_KERNEL_CORE_PAT); cl_log(LOG_WARNING , "Consider setting %s (or equivalent) to" " 1 for maximum supportability", PROC_SYS_KERNEL_CORE_PID); } return 0; } /* * SIGQUIT 3 Core Quit from keyboard * SIGILL 4 Core Illegal Instruction * SIGABRT 6 Core Abort signal from abort(3) * SIGFPE 8 Core Floating point exception * SIGSEGV 11 Core Invalid memory reference * SIGBUS 10,7,10 Core Bus error (bad memory access) * SIGSYS 2,-,12 Core Bad argument to routine (SVID) * SIGTRAP 5 Core Trace/breakpoint trap * SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2 BSD) * SIGXFSZ 25,25,31 Core File size limit exceeded (4.2 BSD) */ /* * This function exists to allow security-sensitive programs * to safely take core dumps. Such programs can't can't call * cl_untaint_coredumps() alone - because it might cause a * leak of confidential information - as information which should * only be known by the "high-privilege" user id will be written * into a core dump which is readable by the "low-privilege" user id. * This is a bad thing. * * This function causes this program to call a special signal handler * on receipt of any core dumping signal. This handler then does * the following four things on receipt of a core dumping signal: * * 1) Set privileges to "maximum" on receipt of a signal * 2) "untaint" themselves with regard to core dumping * 3) set SIG_DFLT for the received signal * 4) Kill themselves with the received core-dumping signal * * Any process *could* do this to get core dumps, but if your stack * is screwed up, then the signal handler might not work. * If you're core dumping because of a stack overflow, it certainly won't work. * * On the other hand, this function may work on some OSes that don't support * prctl(2). This is an untested theory at this time... */ void cl_set_all_coredump_signal_handlers(void) { static const int coresigs [] = {SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV #ifdef SIGBUS , SIGBUS #endif #ifdef SIGSYS , SIGSYS #endif #ifdef SIGTRAP , SIGTRAP #endif #ifdef SIGXCPU , SIGXCPU #endif #ifdef SIGXFSZ , SIGXFSZ #endif }; int j; for (j=0; j < DIMOF(coresigs); ++j) { cl_set_coredump_signal_handler(coresigs[j]); } } /* * See note above about why using this function directly is sometimes * a bad idea, and you might need to use cl_set_all_coredump_signal_handlers() * instead. */ void cl_untaint_coredumps(void) { #if defined(PR_SET_DUMPABLE) prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL); #endif } static void cl_coredump_signal_handler(int nsig) { return_to_orig_privs(); if (geteuid() == 0) { /* Put ALL privileges back to root... */ if (setuid(0) < 0) { cl_perror("cl_coredump_signal_handler: unable to setuid(0)"); } } cl_untaint_coredumps(); /* Do the best we know how to do... */ CL_SIGNAL(nsig, SIG_DFL); kill(getpid(), nsig); } void cl_set_coredump_signal_handler(int nsig) { CL_SIGNAL(nsig, cl_coredump_signal_handler); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/cpulimits.c0000644000000000000000000001352212120057602026343 0ustar00usergroup00000000000000/* * Functions to put dynamic limits on CPU consumption. * * Copyright (C) 2003 IBM Corporation * * Author: * * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ************************************************************************** * * This allows us to better catch runaway realtime processes that * might otherwise hang the whole system (if they're POSIX realtime * processes). * * We do this by getting a "lease" on CPU time, and then extending * the lease every so often as real time elapses. Since we only * extend the lease by a bounded amount computed on the basis of an * upper bound of how much CPU the code is "expected" to consume during * the lease interval, this means that if we go into an infinite * loop, it is highly probable that this will be detected and our * process will be terminated by the operating system with a SIGXCPU. * * If you want to handle this signal, then fine... Do so... * * If not, the default is to terminate the process and produce a core * dump. This is a great default for debugging... * * * The process is basically this: * - Set the CPU percentage limit with cl_cpu_limit_setpercent() * according to what you expect the CPU percentage to top out at * measured over an interval at >= 60 seconds * - Call cl_cpu_limit_ms_interval() to figure out how often to update * the CPU limit (it returns milliseconds) * - At least as often as indicated above, call cl_cpu_limit_update() * to update our current CPU limit. * * These limits are approximate, so be a little conservative. * If you've gone into an infinite loop, it'll likely get caught ;-) * * As of this writing, this code will never set the soft CPU limit less * than four seconds, or greater than 60 seconds. * */ #include #include #include #include #include #include #include static longclock_t nexttimetoupdate; /* How long between checking out CPU usage? */ static int cpuinterval_ms = 0; /* How much cpu (in seconds) allowed at each check interval? */ static int cpusecs; #define ROUND(foo) ((int)((foo)+0.5)) /* * Update our current CPU limit (via setrlimit) according to our * current resource consumption, and our current cpu % limit * * We only set the soft CPU limit, and do not change the maximum * (hard) CPU limit, but we respect it if it's already set. * * As a result, this code can be used by privileged and non-privileged * processes. */ static int update_cpu_interval(void) { struct rusage ru; struct rlimit rlim; unsigned long timesecs; unsigned long microsec; /* Compute how much CPU we've used so far... */ getrusage(RUSAGE_SELF, &ru); timesecs = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec; microsec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec; /* Round up to the next higher second */ if (microsec > 1000000) { timesecs += 2; }else{ timesecs += 1; } /* Compute our next CPU limit */ timesecs += cpusecs; /* Figure out when we next need to update our CPU limit */ nexttimetoupdate = add_longclock(time_longclock() , msto_longclock(cpuinterval_ms)); getrlimit(RLIMIT_CPU, &rlim); /* Make sure we don't exceed the hard CPU limit (if set) */ if (rlim.rlim_max != RLIM_INFINITY && timesecs > rlim.rlim_max) { timesecs = rlim.rlim_max; } #if 0 cl_log(LOG_DEBUG , "Setting max CPU limit to %ld seconds", timesecs); #endif /* Update the OS-level soft CPU limit */ rlim.rlim_cur = timesecs; return setrlimit(RLIMIT_CPU, &rlim); } #define MININTERVAL 60 /* seconds */ int cl_cpu_limit_setpercent(int ipercent) { float percent; int interval; if (ipercent > 99) { ipercent = 99; } if (ipercent < 1) { ipercent = 1; } percent = ipercent; percent /= (float)100; interval= MININTERVAL; /* * Compute how much CPU we will allow to be used * for each check interval. * * Rules: * - we won't require checking more often than * every 60 seconds * - we won't limit ourselves to less than * 4 seconds of CPU per checking interval */ for (;;) { cpusecs = ROUND((float)interval*percent); if (cpusecs >= 4) { break; } interval *= 2; } /* * Now compute how long to go between updates to our CPU limit * from the perspective of the OS (via setrlimit(2)). * * We do the computation this way because the CPU limit * can only be set to the nearest second, but timers can * generally be set more accurately. */ cpuinterval_ms = (int)(((float)cpusecs / percent)*1000.0); cl_log(LOG_DEBUG , "Limiting CPU: %d CPU seconds every %d milliseconds" , cpusecs, cpuinterval_ms); return update_cpu_interval(); } int cl_cpu_limit_ms_interval(void) { return cpuinterval_ms; } int cl_cpu_limit_update(void) { longclock_t now = time_longclock(); long msleft; if (cpuinterval_ms <= 0) { return 0; } if (cmp_longclock(now, nexttimetoupdate) > 0) { return update_cpu_interval(); } msleft = longclockto_ms(sub_longclock(nexttimetoupdate, now)); if (msleft < 500) { return update_cpu_interval(); } return 0; } int cl_cpu_limit_disable(void) { struct rlimit rlim; getrlimit(RLIMIT_CPU, &rlim); rlim.rlim_cur = rlim.rlim_max; return setrlimit(RLIMIT_CPU, &rlim); } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipcsocket.c0000644000000000000000000020444112120057602026320 0ustar00usergroup00000000000000/* * ipcsocket unix domain socket implementation of IPC abstraction. * * Copyright (c) 2002 Xiaoxiang Liu * * Stream support (c) 2004,2006 David Lee * Note: many of the variable/function names "*socket*" should be * interpreted as having a more generic "ipc-channel-type" meaning. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include /* avoid including cib.h - used in gshi's "late message" code to avoid * printing insanely large messages */ #define F_CIB_CALLDATA "cib_calldata" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H # include #endif #ifdef HAVE_SYS_SYSLIMITS_H # include #endif #ifdef HAVE_SYS_CRED_H # include #endif #ifdef HAVE_SYS_UCRED_H # include #endif /* For 'getpeerucred()' (Solaris 10 upwards) */ #ifdef HAVE_UCRED_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif /* * Normally use "socket" code. But on some OSes alternatives may be * preferred (or necessary). */ #define HB_IPC_SOCKET 1 #define HB_IPC_STREAM 2 /* #define HB_IPC_ANOTHER 3 */ #ifndef HB_IPC_METHOD # if defined(SO_PEERCRED) || defined(HAVE_GETPEEREID) \ || defined(SCM_CREDS) || defined(HAVE_GETPEERUCRED) # define HB_IPC_METHOD HB_IPC_SOCKET # elif defined(HAVE_STROPTS_H) # define HB_IPC_METHOD HB_IPC_STREAM # else # error. Surely we have sockets or streams... # endif #endif #if HB_IPC_METHOD == HB_IPC_SOCKET # include # include # include #elif HB_IPC_METHOD == HB_IPC_STREAM # include #else # error "IPC type invalid" #endif #include #include #include #include #ifndef UNIX_PATH_MAX # define UNIX_PATH_MAX 108 #endif #if HB_IPC_METHOD == HB_IPC_SOCKET # define MAX_LISTEN_NUM 10 # ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 # endif # ifndef AF_LOCAL # define AF_LOCAL AF_UNIX # endif #endif /* HB_IPC_METHOD */ /*********************************************************************** * * Determine the IPC authentication scheme... More machine dependent than * we'd like, but don't know any better way... * ***********************************************************************/ #ifdef SO_PEERCRED # define USE_SO_PEERCRED #elif HAVE_GETPEEREID # define USE_GETPEEREID #elif defined(SCM_CREDS) # define USE_SCM_CREDS #elif HAVE_GETPEERUCRED /* e.g. Solaris 10 upwards */ # define USE_GETPEERUCRED #elif HB_IPC_METHOD == HB_IPC_STREAM # define USE_STREAM_CREDS #else # define USE_DUMMY_CREDS /* This will make it compile, but attempts to authenticate * will fail. This is a stopgap measure ;-) */ #endif #if HB_IPC_METHOD == HB_IPC_SOCKET # ifdef USE_BINDSTAT_CREDS # ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (offsetof (sockaddr_un, sun_path) + strlen ((ptr)->sun_path)) # endif # endif #endif /* HB_IPC_METHOD */ /* wait connection private data. */ struct SOCKET_WAIT_CONN_PRIVATE{ /* the path name wich the connection will be built on. */ char path_name[UNIX_PATH_MAX]; #if HB_IPC_METHOD == HB_IPC_SOCKET /* the domain socket. */ int s; #elif HB_IPC_METHOD == HB_IPC_STREAM /* the streams pipe */ int pipefds[2]; #endif }; /* channel private data. */ struct SOCKET_CH_PRIVATE{ /* the path name wich the connection will be built on. */ char path_name[UNIX_PATH_MAX]; /* the domain socket. */ int s; /* the size of expecting data for below buffered message buf_msg */ int remaining_data; #if HB_IPC_METHOD == HB_IPC_SOCKET /* The address of our peer - used by USE_BINDSTAT_CREDS version of * socket_verify_auth() */ struct sockaddr_un *peer_addr; #elif HB_IPC_METHOD == HB_IPC_STREAM uid_t farside_uid; gid_t farside_gid; #endif /* the buf used to save unfinished message */ struct IPC_MESSAGE *buf_msg; }; struct IPC_Stats { long nsent; long noutqueued; long send_count; long nreceived; long ninqueued; long recv_count; int last_recv_errno; int last_recv_rc; int last_send_errno; int last_send_rc; }; static struct IPC_Stats SocketIPCStats = {0, 0, 0, 0}; extern int debug_level; /* unix domain socket implementations of IPC functions. */ static int socket_resume_io(struct IPC_CHANNEL *ch); static struct IPC_MESSAGE* socket_message_new(struct IPC_CHANNEL*ch , int msg_len); struct IPC_WAIT_CONNECTION *socket_wait_conn_new(GHashTable* ch_attrs); /* *** FIXME: This is also declared in 'ocf_ipc.c'. */ struct IPC_CHANNEL* socket_client_channel_new(GHashTable *attrs); static struct IPC_CHANNEL* socket_server_channel_new(int sockfd); static struct IPC_CHANNEL * channel_new(int sockfd, int conntype, const char *pathname); static int client_channel_new_auth(int sockfd); static int verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid); typedef void (*DelProc)(IPC_Message*); static struct IPC_MESSAGE * ipcmsg_new(struct IPC_CHANNEL* ch, const void* data, int len, void* private, DelProc d); static pid_t socket_get_farside_pid(int sockfd); extern int (*ipc_pollfunc_ptr)(struct pollfd *, nfds_t, int); static int socket_resume_io_read(struct IPC_CHANNEL *ch, int*, gboolean read1anyway); static struct IPC_OPS socket_ops; static gboolean ipc_time_debug_flag = TRUE; void set_ipc_time_debug_flag(gboolean flag) { ipc_time_debug_flag = flag; } #ifdef IPC_TIME_DEBUG extern struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag); void cl_log_message (int log_level, const struct ha_msg *m); int timediff(longclock_t t1, longclock_t t2); void ha_msg_del(struct ha_msg* msg); void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos); #define SET_ENQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->enqueue_time, &t, sizeof(longclock_t)) #define SET_SEND_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->send_time, &t, sizeof(longclock_t)) #define SET_RECV_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->recv_time, &t, sizeof(longclock_t)) #define SET_DEQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->dequeue_time, &t, sizeof(longclock_t)) static longclock_t get_enqueue_time(IPC_Message *ipcmsg) { longclock_t t; memcpy(&t, &(((struct SOCKET_MSG_HEAD *)ipcmsg->msg_buf)->enqueue_time), sizeof(longclock_t)); return t; } int timediff(longclock_t t1, longclock_t t2) { longclock_t remain; remain = sub_longclock(t1, t2); return longclockto_ms(remain); } void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos) { int msdiff = 0; longclock_t lnow = time_longclock(); char positions[4][16]={ "enqueue", "send", "recv", "dequeue"}; if (ipc_time_debug_flag == FALSE) { return ; } if (ipcmsg->msg_body == NULL || ipcmsg->msg_buf == NULL) { cl_log(LOG_ERR, "msg_body =%p, msg_bu=%p", ipcmsg->msg_body, ipcmsg->msg_buf); abort(); return; } switch(whichpos) { case MSGPOS_ENQUEUE: SET_ENQUEUE_TIME(ipcmsg, lnow); break; case MSGPOS_SEND: SET_SEND_TIME(ipcmsg, lnow); goto checktime; case MSGPOS_RECV: SET_RECV_TIME(ipcmsg, lnow); goto checktime; case MSGPOS_DEQUEUE: SET_DEQUEUE_TIME(ipcmsg, lnow); checktime: msdiff = timediff(lnow, get_enqueue_time(ipcmsg)); if (msdiff > MAXIPCTIME) { struct ha_msg* hamsg = NULL; cl_log(LOG_WARNING, " message delayed from enqueue to %s %d ms " "(enqueue-time=%lu, peer pid=%d) ", positions[whichpos], msdiff, longclockto_ms(get_enqueue_time(ipcmsg)), ch->farside_pid); (void)hamsg; #if 0 hamsg = wirefmt2msg(ipcmsg->msg_body, ipcmsg->msg_len, 0); if (hamsg != NULL) { struct ha_msg *crm_data = NULL; crm_data = cl_get_struct( hamsg, F_CRM_DATA); if(crm_data == NULL) { crm_data = cl_get_struct( hamsg, F_CIB_CALLDATA); } if(crm_data != NULL) { cl_msg_remove_value( hamsg, crm_data); } cl_log_message(LOG_DEBUG, hamsg); ha_msg_del(hamsg); } else { if (!ipcmsg) { cl_log(LOG_ERR, "IPC msg 0x%lx is unallocated" , (gulong)ipcmsg); return; } if (!ipcmsg->msg_body) { cl_log(LOG_ERR, "IPC msg body 0x%lx is unallocated" , (gulong)ipcmsg->msg_body); return; } } #endif } break; default: cl_log(LOG_ERR, "wrong position value in IPC:%d", whichpos); return; } } #endif void dump_ipc_info(const IPC_Channel* chan); #undef AUDIT_CHANNELS #ifndef AUDIT_CHANNELS # define CHANAUDIT(ch) /*NOTHING */ #else # define CHANAUDIT(ch) socket_chan_audit(ch) # define MAXPID 65535 static void socket_chan_audit(const struct IPC_CHANNEL* ch) { int badch = FALSE; struct SOCKET_CH_PRIVATE *chp; struct stat b; if ((chp = ch->ch_private) == NULL) { cl_log(LOG_CRIT, "Bad ch_private"); badch = TRUE; } if (ch->ops != &socket_ops) { cl_log(LOG_CRIT, "Bad socket_ops"); badch = TRUE; } if (ch->ch_status == IPC_DISCONNECT) { return; } if (!IPC_ISRCONN(ch)) { cl_log(LOG_CRIT, "Bad ch_status [%d]", ch->ch_status); badch = TRUE; } if (ch->farside_pid < 0 || ch->farside_pid > MAXPID) { cl_log(LOG_CRIT, "Bad farside_pid"); badch = TRUE; } if (fstat(chp->s, &b) < 0) { badch = TRUE; } else if ((b.st_mode & S_IFMT) != S_IFSOCK) { cl_log(LOG_CRIT, "channel @ 0x%lx: not a socket" , (unsigned long)ch); badch = TRUE; } if (chp->remaining_data < 0) { cl_log(LOG_CRIT, "Negative remaining_data"); badch = TRUE; } if (chp->remaining_data < 0 || chp->remaining_data > MAXMSG) { cl_log(LOG_CRIT, "Excessive/bad remaining_data"); badch = TRUE; } if (chp->remaining_data && chp->buf_msg == NULL) { cl_log(LOG_CRIT , "inconsistent remaining_data [%ld]/buf_msg[0x%lx]" , (long)chp->remaining_data, (unsigned long)chp->buf_msg); badch = TRUE; } if (chp->remaining_data == 0 && chp->buf_msg != NULL) { cl_log(LOG_CRIT , "inconsistent remaining_data [%ld]/buf_msg[0x%lx] (2)" , (long)chp->remaining_data, (unsigned long)chp->buf_msg); badch = TRUE; } if (ch->send_queue == NULL || ch->recv_queue == NULL) { cl_log(LOG_CRIT, "bad send/recv queue"); badch = TRUE; } if (ch->recv_queue->current_qlen < 0 || ch->recv_queue->current_qlen > ch->recv_queue->max_qlen) { cl_log(LOG_CRIT, "bad recv queue"); badch = TRUE; } if (ch->send_queue->current_qlen < 0 || ch->send_queue->current_qlen > ch->send_queue->max_qlen) { cl_log(LOG_CRIT, "bad send_queue"); badch = TRUE; } if (badch) { cl_log(LOG_CRIT, "Bad channel @ 0x%lx", (unsigned long)ch); dump_ipc_info(ch); abort(); } } #endif #ifdef CHEAT_CHECKS long SeqNums[32]; static long cheat_get_sequence(IPC_Message* msg) { const char header [] = "String-"; size_t header_len = sizeof(header)-1; char * body; if (msg == NULL || msg->msg_len < sizeof(header) || msg->msg_len > sizeof(header) + 10 || strncmp(msg->msg_body, header, header_len) != 0) { return -1L; } body = msg->msg_body; return atol(body+header_len); } static char SavedReadBody[32]; static char SavedReceivedBody[32]; static char SavedQueuedBody[32]; static char SavedSentBody[32]; #ifndef MIN # define MIN(a,b) (a < b ? a : b) #endif static void save_body(struct IPC_MESSAGE *msg, char * savearea, size_t length) { int mlen = strnlen(msg->msg_body, MIN(length, msg->msg_len)); memcpy(savearea, msg->msg_body, mlen); savearea[mlen] = EOS; } static void audit_readmsgq_msg(gpointer msg, gpointer user_data) { long cheatseq = cheat_get_sequence(msg); if (cheatseq < SeqNums[1] || cheatseq > SeqNums[2]) { cl_log(LOG_ERR , "Read Q Message %ld not in range [%ld:%ld]" , cheatseq, SeqNums[1], SeqNums[2]); } } static void saveandcheck(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg, char * savearea , size_t savesize, long* lastseq, const char * text) { long cheatseq = cheat_get_sequence(msg); save_body(msg, savearea, savesize); if (*lastseq != 0 ) { if (cheatseq != *lastseq +1) { int j; cl_log(LOG_ERR , "%s packets out of sequence! %ld versus %ld [pid %d]" , text, cheatseq, *lastseq, (int)getpid()); dump_ipc_info(ch); for (j=0; j < 4; ++j) { cl_log(LOG_DEBUG , "SeqNums[%d] = %ld" , j, SeqNums[j]); } cl_log(LOG_ERR , "SocketIPCStats.nsent = %ld" , SocketIPCStats.nsent); cl_log(LOG_ERR , "SocketIPCStats.noutqueued = %ld" , SocketIPCStats.noutqueued); cl_log(LOG_ERR , "SocketIPCStats.nreceived = %ld" , SocketIPCStats.nreceived); cl_log(LOG_ERR , "SocketIPCStats.ninqueued = %ld" , SocketIPCStats.ninqueued); } } g_list_foreach(ch->recv_queue->queue, audit_readmsgq_msg, NULL); if (cheatseq > 0) { *lastseq = cheatseq; } } # define CHECKFOO(which, ch, msg, area, text) { \ saveandcheck(ch,msg,area,sizeof(area),SeqNums+which,text); \ } #else # define CHECKFOO(which, ch, msg, area, text) /* Nothing */ #endif static void dump_msg(struct IPC_MESSAGE *msg, const char * label) { #ifdef CHEAT_CHECKS cl_log(LOG_DEBUG, "%s packet (length %d) [%s] %ld pid %d" , label, (int)msg->msg_len, (char*)msg->msg_body , cheat_get_sequence(msg), (int)getpid()); #else cl_log(LOG_DEBUG, "%s length %d [%s] pid %d" , label, (int)msg->msg_len, (char*)msg->msg_body , (int)getpid()); #endif } static void dump_msgq_msg(gpointer data, gpointer user_data) { dump_msg(data, user_data); } void dump_ipc_info(const IPC_Channel* chan) { char squeue[] = "Send queue"; char rqueue[] = "Receive queue"; #ifdef CHEAT_CHECKS cl_log(LOG_DEBUG, "Saved Last Body read[%s]", SavedReadBody); cl_log(LOG_DEBUG, "Saved Last Body received[%s]", SavedReceivedBody); cl_log(LOG_DEBUG, "Saved Last Body Queued[%s]", SavedQueuedBody); cl_log(LOG_DEBUG, "Saved Last Body Sent[%s]", SavedSentBody); #endif g_list_foreach(chan->send_queue->queue, dump_msgq_msg, squeue); g_list_foreach(chan->recv_queue->queue, dump_msgq_msg, rqueue); CHANAUDIT(chan); } /* destroy socket wait channel */ static void socket_destroy_wait_conn(struct IPC_WAIT_CONNECTION * wait_conn) { struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private; if (wc != NULL) { #if HB_IPC_METHOD == HB_IPC_SOCKET if (wc->s >= 0) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: closing socket %d" , __FUNCTION__, wc->s); } close(wc->s); cl_poll_ignore(wc->s); unlink(wc->path_name); wc->s = -1; } #elif HB_IPC_METHOD == HB_IPC_STREAM cl_poll_ignore(wc->pipefds[0]); if (wc->pipefds[0] >= 0) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: closing pipe[0] %d" , __FUNCTION__, wc->pipefds[0]); } wc->pipefds[0] = -1; } if (wc->pipefds[1] >= 0) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: closing pipe[1] %d" , __FUNCTION__, wc->pipefds[1]); } wc->pipefds[0] = -1; } unlink(wc->path_name); #endif g_free(wc); } g_free((void*) wait_conn); } /* return a fd which can be listened on for new connections. */ static int socket_wait_selectfd(struct IPC_WAIT_CONNECTION *wait_conn) { struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private; #if HB_IPC_METHOD == HB_IPC_SOCKET return (wc == NULL ? -1 : wc->s); #elif HB_IPC_METHOD == HB_IPC_STREAM return (wc == NULL ? -1 : wc->pipefds[0]); #endif } /* socket accept connection. */ static struct IPC_CHANNEL* socket_accept_connection(struct IPC_WAIT_CONNECTION * wait_conn , struct IPC_AUTH *auth_info) { struct IPC_CHANNEL * ch = NULL; int s; int new_sock; struct SOCKET_WAIT_CONN_PRIVATE* conn_private; struct SOCKET_CH_PRIVATE * ch_private ; int auth_result = IPC_FAIL; int saveerrno=errno; gboolean was_error = FALSE; #if HB_IPC_METHOD == HB_IPC_SOCKET /* make peer_addr a pointer so it can be used by the * USE_BINDSTAT_CREDS implementation of socket_verify_auth() */ struct sockaddr_un * peer_addr = NULL; socklen_t sin_size; #elif HB_IPC_METHOD == HB_IPC_STREAM struct strrecvfd strrecvfd; #endif /* get select fd */ s = wait_conn->ops->get_select_fd(wait_conn); if (s < 0) { cl_log(LOG_ERR, "get_select_fd: invalid fd"); return NULL; } /* Get client connection. */ #if HB_IPC_METHOD == HB_IPC_SOCKET peer_addr = g_new(struct sockaddr_un, 1); *peer_addr->sun_path = '\0'; sin_size = sizeof(struct sockaddr_un); new_sock = accept(s, (struct sockaddr *)peer_addr, &sin_size); #elif HB_IPC_METHOD == HB_IPC_STREAM if (ioctl(s, I_RECVFD, &strrecvfd) == -1) { new_sock = -1; } else { new_sock = strrecvfd.fd; } #endif saveerrno=errno; if (new_sock == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { cl_perror("socket_accept_connection: accept(sock=%d)" , s); } was_error = TRUE; } else { if ((ch = socket_server_channel_new(new_sock)) == NULL) { cl_log(LOG_ERR , "socket_accept_connection:" " Can't create new channel"); was_error = TRUE; } else { conn_private=(struct SOCKET_WAIT_CONN_PRIVATE*) ( wait_conn->ch_private); ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private); strncpy(ch_private->path_name,conn_private->path_name , sizeof(conn_private->path_name)); #if HB_IPC_METHOD == HB_IPC_SOCKET ch_private->peer_addr = peer_addr; #elif HB_IPC_METHOD == HB_IPC_STREAM ch_private->farside_uid = strrecvfd.uid; ch_private->farside_gid = strrecvfd.gid; #endif } } /* Verify the client authorization information. */ if(was_error == FALSE) { auth_result = ch->ops->verify_auth(ch, auth_info); if (auth_result == IPC_OK) { ch->ch_status = IPC_CONNECT; ch->farside_pid = socket_get_farside_pid(new_sock); return ch; } saveerrno=errno; } #if HB_IPC_METHOD == HB_IPC_SOCKET g_free(peer_addr); peer_addr = NULL; #endif errno=saveerrno; return NULL; } /* * Called by socket_destroy(). Disconnect the connection * and set ch_status to IPC_DISCONNECT. * * parameters : * ch (IN) the pointer to the channel. * * return values : * IPC_OK the connection is disconnected successfully. * IPC_FAIL operation fails. */ static int socket_disconnect(struct IPC_CHANNEL* ch) { struct SOCKET_CH_PRIVATE* conn_info; conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private; if (debug_level > 1) { cl_log(LOG_DEBUG , "%s(sock=%d, ch=0x%lx){" , __FUNCTION__ , conn_info->s, (unsigned long)ch); } #if 0 if (ch->ch_status != IPC_DISCONNECT) { cl_log(LOG_INFO, "forced disconnect for fd %d", conn_info->s); } #endif if (ch->ch_status == IPC_CONNECT) { socket_resume_io(ch); } if (conn_info->s >= 0) { if (debug_level > 1) { cl_log(LOG_DEBUG , "%s: closing socket %d" , __FUNCTION__, conn_info->s); } close(conn_info->s); cl_poll_ignore(conn_info->s); conn_info->s = -1; } ch->ch_status = IPC_DISCONNECT; if (debug_level > 1) { cl_log(LOG_DEBUG, "}/*%s(sock=%d, ch=0x%lx)*/" , __FUNCTION__, conn_info->s, (unsigned long)ch); } return IPC_OK; } /* * destroy a ipc queue and clean all memory space assigned to this queue. * parameters: * q (IN) the pointer to the queue which should be destroied. * * FIXME: This function does not free up messages that might * be in the queue. */ static void socket_destroy_queue(struct IPC_QUEUE * q) { g_list_free(q->queue); g_free((void *) q); } static void socket_destroy_channel(struct IPC_CHANNEL * ch) { --ch->refcount; if (ch->refcount > 0) { return; } if (ch->ch_status == IPC_CONNECT) { socket_resume_io(ch); } if (debug_level > 1) { cl_log(LOG_DEBUG, "socket_destroy(ch=0x%lx){" , (unsigned long)ch); } socket_disconnect(ch); socket_destroy_queue(ch->send_queue); socket_destroy_queue(ch->recv_queue); if (ch->pool) { ipc_bufpool_unref(ch->pool); } if (ch->ch_private != NULL) { #if HB_IPC_METHOD == HB_IPC_SOCKET struct SOCKET_CH_PRIVATE *priv = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if(priv->peer_addr != NULL) { if (*priv->peer_addr->sun_path) { unlink(priv->peer_addr->sun_path); } g_free((void*)(priv->peer_addr)); } #endif g_free((void*)(ch->ch_private)); } memset(ch, 0xff, sizeof(*ch)); g_free((void*)ch); if (debug_level > 1) { cl_log(LOG_DEBUG, "}/*socket_destroy(ch=0x%lx)*/" , (unsigned long)ch); } } static int socket_check_disc_pending(struct IPC_CHANNEL* ch) { int rc; struct pollfd sockpoll; if (ch->ch_status == IPC_DISCONNECT) { cl_log(LOG_ERR, "check_disc_pending() already disconnected"); return IPC_BROKEN; } if (ch->recv_queue->current_qlen > 0) { return IPC_OK; } sockpoll.fd = ch->ops->get_recv_select_fd(ch); sockpoll.events = POLLIN; rc = ipc_pollfunc_ptr(&sockpoll, 1, 0); if (rc < 0) { cl_log(LOG_INFO , "socket_check_disc_pending() bad poll call"); ch->ch_status = IPC_DISCONNECT; return IPC_BROKEN; } if (sockpoll.revents & POLLHUP) { if (sockpoll.revents & POLLIN) { ch->ch_status = IPC_DISC_PENDING; } else { #if 1 cl_log(LOG_INFO, "HUP without input"); #endif ch->ch_status = IPC_DISCONNECT; return IPC_BROKEN; } } if (sockpoll.revents & POLLIN) { int dummy; socket_resume_io_read(ch, &dummy, FALSE); } return IPC_OK; } static int socket_initiate_connection(struct IPC_CHANNEL * ch) { struct SOCKET_CH_PRIVATE* conn_info; #if HB_IPC_METHOD == HB_IPC_SOCKET struct sockaddr_un peer_addr; /* connector's address information */ #elif HB_IPC_METHOD == HB_IPC_STREAM #endif conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private; #if HB_IPC_METHOD == HB_IPC_SOCKET /* Prepare the socket */ memset(&peer_addr, 0, sizeof(peer_addr)); peer_addr.sun_family = AF_LOCAL; /* host byte order */ if (strlen(conn_info->path_name) >= sizeof(peer_addr.sun_path)) { return IPC_FAIL; } strncpy(peer_addr.sun_path, conn_info->path_name, sizeof(peer_addr.sun_path)); /* Send connection request */ if (connect(conn_info->s, (struct sockaddr *)&peer_addr , sizeof(struct sockaddr_un)) == -1) { return IPC_FAIL; } #elif HB_IPC_METHOD == HB_IPC_STREAM #endif ch->ch_status = IPC_CONNECT; ch->farside_pid = socket_get_farside_pid(conn_info->s); return IPC_OK; } static void socket_set_high_flow_callback(IPC_Channel* ch, flow_callback_t callback, void* userdata) { ch->high_flow_callback = callback; ch->high_flow_userdata = userdata; } static void socket_set_low_flow_callback(IPC_Channel* ch, flow_callback_t callback, void* userdata) { ch->low_flow_callback = callback; ch->low_flow_userdata = userdata; } static void socket_check_flow_control(struct IPC_CHANNEL* ch, int orig_qlen, int curr_qlen) { if (!IPC_ISRCONN(ch)) { return; } if (curr_qlen >= ch->high_flow_mark && ch->high_flow_callback) { ch->high_flow_callback(ch, ch->high_flow_userdata); } if (curr_qlen <= ch->low_flow_mark && orig_qlen > ch->low_flow_mark && ch->low_flow_callback) { ch->low_flow_callback(ch, ch->low_flow_userdata); } } static int socket_send(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg) { int orig_qlen; int diff; struct IPC_MESSAGE* newmsg; if (msg->msg_len > MAXMSG) { cl_log(LOG_ERR, "%s: sorry, cannot send messages " "bigger than %d (requested %lu)", __FUNCTION__, MAXMSG, (unsigned long)msg->msg_len); return IPC_FAIL; } if (msg->msg_len < 0) { cl_log(LOG_ERR, "socket_send: " "invalid message"); return IPC_FAIL; } if (ch->ch_status != IPC_CONNECT) { return IPC_FAIL; } ch->ops->resume_io(ch); if (ch->send_queue->maxqlen_cnt && time(NULL) - ch->send_queue->last_maxqlen_warn >= 60) { cl_log(LOG_ERR, "%u messages dropped on a non-blocking channel (send queue maximum length %d)", ch->send_queue->maxqlen_cnt, (int)ch->send_queue->max_qlen); ch->send_queue->maxqlen_cnt = 0; } if ( !ch->should_send_block && ch->send_queue->current_qlen >= ch->send_queue->max_qlen) { if (!ch->send_queue->maxqlen_cnt) { ch->send_queue->last_maxqlen_warn = time(NULL); } ch->send_queue->maxqlen_cnt++; if (ch->should_block_fail) { return IPC_FAIL; } else { return IPC_OK; } } while (ch->send_queue->current_qlen >= ch->send_queue->max_qlen) { if (ch->ch_status != IPC_CONNECT) { cl_log(LOG_WARNING, "socket_send:" " message queue exceeded and IPC not connected"); return IPC_FAIL; } cl_shortsleep(); ch->ops->resume_io(ch); } /* add the message into the send queue */ CHECKFOO(0,ch, msg, SavedQueuedBody, "queued message"); SocketIPCStats.noutqueued++; diff = 0; if (msg->msg_buf ) { diff = (char*)msg->msg_body - (char*)msg->msg_buf; } if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) { /* either we don't have msg->msg_buf set * or we don't have enough bytes for socket head * we delete this message and creates * a new one and delete the old one */ newmsg= socket_message_new(ch, msg->msg_len); if (newmsg == NULL) { cl_log(LOG_ERR, "socket_resume_io_write: " "allocating memory for new ipc msg failed"); return IPC_FAIL; } memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len); if(msg->msg_done) { msg->msg_done(msg); }; msg = newmsg; } #ifdef IPC_TIME_DEBUG ipc_time_debug(ch,msg, MSGPOS_ENQUEUE); #endif ch->send_queue->queue = g_list_append(ch->send_queue->queue, msg); orig_qlen = ch->send_queue->current_qlen++; socket_check_flow_control(ch, orig_qlen, orig_qlen +1 ); /* resume io */ ch->ops->resume_io(ch); return IPC_OK; } static int socket_recv(struct IPC_CHANNEL * ch, struct IPC_MESSAGE** message) { GList *element; int nbytes; int result; socket_resume_io(ch); result = socket_resume_io_read(ch, &nbytes, TRUE); *message = NULL; if (ch->recv_queue->current_qlen == 0) { return result != IPC_OK ? result : IPC_FAIL; /*return IPC_OK;*/ } element = g_list_first(ch->recv_queue->queue); if (element == NULL) { /* Internal accounting error, but correctable */ cl_log(LOG_ERR , "recv failure: qlen (%ld) > 0, but no message found." , (long)ch->recv_queue->current_qlen); ch->recv_queue->current_qlen = 0; return IPC_FAIL; } *message = (struct IPC_MESSAGE *) (element->data); #ifdef IPC_TIME_DEBUG ipc_time_debug(ch, *message, MSGPOS_DEQUEUE); #endif CHECKFOO(1,ch, *message, SavedReadBody, "read message"); SocketIPCStats.nreceived++; ch->recv_queue->queue = g_list_remove(ch->recv_queue->queue , element->data); ch->recv_queue->current_qlen--; return IPC_OK; } static int socket_check_poll(struct IPC_CHANNEL * ch , struct pollfd * sockpoll) { if (ch->ch_status == IPC_DISCONNECT) { return IPC_OK; } if (sockpoll->revents & POLLHUP) { /* If input present, or this is an output-only poll... */ if (sockpoll->revents & POLLIN || (sockpoll-> events & POLLIN) == 0 ) { ch->ch_status = IPC_DISC_PENDING; return IPC_OK; } #if 1 cl_log(LOG_INFO, "socket_check_poll(): HUP without input"); #endif ch->ch_status = IPC_DISCONNECT; return IPC_BROKEN; } else if (sockpoll->revents & (POLLNVAL|POLLERR)) { /* Have we already closed the socket? */ if (fcntl(sockpoll->fd, F_GETFL) < 0) { cl_perror("socket_check_poll(pid %d): bad fd [%d]" , (int) getpid(), sockpoll->fd); ch->ch_status = IPC_DISCONNECT; return IPC_OK; } cl_log(LOG_ERR , "revents failure: fd %d, flags 0x%x" , sockpoll->fd, sockpoll->revents); errno = EINVAL; return IPC_FAIL; } return IPC_OK; } static int socket_waitfor(struct IPC_CHANNEL * ch , gboolean (*finished)(struct IPC_CHANNEL * ch)) { struct pollfd sockpoll; CHANAUDIT(ch); if (finished(ch)) { return IPC_OK; } if (ch->ch_status == IPC_DISCONNECT) { return IPC_BROKEN; } sockpoll.fd = ch->ops->get_recv_select_fd(ch); while (!finished(ch) && IPC_ISRCONN(ch)) { int rc; sockpoll.events = POLLIN; /* Cannot call resume_io after the call to finished() * and before the call to poll because we might * change the state of the thing finished() is * waiting for. * This means that the poll call below would be * not only pointless, but might * make us hang forever waiting for this * event which has already happened */ if (ch->send_queue->current_qlen > 0) { sockpoll.events |= POLLOUT; } rc = ipc_pollfunc_ptr(&sockpoll, 1, -1); if (rc < 0) { return (errno == EINTR ? IPC_INTR : IPC_FAIL); } rc = socket_check_poll(ch, &sockpoll); if (sockpoll.revents & POLLIN) { socket_resume_io(ch); } if (rc != IPC_OK) { CHANAUDIT(ch); return rc; } } CHANAUDIT(ch); return IPC_OK; } static int socket_waitin(struct IPC_CHANNEL * ch) { return socket_waitfor(ch, ch->ops->is_message_pending); } static gboolean socket_is_output_flushed(struct IPC_CHANNEL * ch) { return ! ch->ops->is_sending_blocked(ch); } static int socket_waitout(struct IPC_CHANNEL * ch) { int rc; CHANAUDIT(ch); rc = socket_waitfor(ch, socket_is_output_flushed); if (rc != IPC_OK) { cl_log(LOG_ERR , "socket_waitout failure: rc = %d", rc); } else if (ch->ops->is_sending_blocked(ch)) { cl_log(LOG_ERR, "socket_waitout output still blocked"); } CHANAUDIT(ch); return rc; } static gboolean socket_is_message_pending(struct IPC_CHANNEL * ch) { int nbytes; socket_resume_io_read(ch, &nbytes, TRUE); ch->ops->resume_io(ch); if (ch->recv_queue->current_qlen > 0) { return TRUE; } return !IPC_ISRCONN(ch); } static gboolean socket_is_output_pending(struct IPC_CHANNEL * ch) { socket_resume_io(ch); return ch->ch_status == IPC_CONNECT && ch->send_queue->current_qlen > 0; } static gboolean socket_is_sendq_full(struct IPC_CHANNEL * ch) { ch->ops->resume_io(ch); return(ch->send_queue->current_qlen == ch->send_queue->max_qlen); } static gboolean socket_is_recvq_full(struct IPC_CHANNEL * ch) { ch->ops->resume_io(ch); return(ch->recv_queue->current_qlen == ch->recv_queue->max_qlen); } static int socket_get_conntype(struct IPC_CHANNEL* ch) { return ch->conntype; } static int socket_assert_auth(struct IPC_CHANNEL *ch, GHashTable *auth) { cl_log(LOG_ERR , "the assert_auth function for domain socket is not implemented"); return IPC_FAIL; } static int socket_resume_io_read(struct IPC_CHANNEL *ch, int* nbytes, gboolean read1anyway) { struct SOCKET_CH_PRIVATE* conn_info; int retcode = IPC_OK; struct pollfd sockpoll; int debug_loopcount = 0; int debug_bytecount = 0; size_t maxqlen = ch->recv_queue->max_qlen; struct ipc_bufpool* pool = ch->pool; int nmsgs = 0; int spaceneeded; *nbytes = 0; CHANAUDIT(ch); conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (ch->ch_status == IPC_DISCONNECT) { return IPC_BROKEN; } if (pool == NULL) { ch->pool = pool = ipc_bufpool_new(0); if (pool == NULL) { cl_log(LOG_ERR, "socket_resume_io_read: " "memory allocation for ipc pool failed"); return IPC_FAIL; } } if (ipc_bufpool_full(pool, ch, &spaceneeded)) { struct ipc_bufpool* newpool; newpool = ipc_bufpool_new(spaceneeded); if (newpool == NULL) { cl_log(LOG_ERR, "socket_resume_io_read: " "memory allocation for a new ipc pool failed"); return IPC_FAIL; } ipc_bufpool_partial_copy(newpool, pool); ipc_bufpool_unref(pool); ch->pool = pool = newpool; } if (maxqlen <= 0 && read1anyway) { maxqlen = 1; } if (ch->recv_queue->current_qlen < maxqlen && retcode == IPC_OK) { void * msg_begin; int msg_len; int len; #if HB_IPC_METHOD == HB_IPC_STREAM struct strbuf d; int flags, rc; #endif CHANAUDIT(ch); ++debug_loopcount; len = ipc_bufpool_spaceleft(pool); msg_begin = pool->currpos; CHANAUDIT(ch); /* Now try to receive some data */ #if HB_IPC_METHOD == HB_IPC_SOCKET msg_len = recv(conn_info->s, msg_begin, len, MSG_DONTWAIT); #elif HB_IPC_METHOD == HB_IPC_STREAM d.maxlen = len; d.len = 0; d.buf = msg_begin; flags = 0; rc = getmsg(conn_info->s, NULL, &d, &flags); msg_len = (rc < 0) ? rc : d.len; #endif SocketIPCStats.last_recv_rc = msg_len; SocketIPCStats.last_recv_errno = errno; ++SocketIPCStats.recv_count; /* Did we get an error? */ if (msg_len < 0) { switch (errno) { case EAGAIN: if (ch->ch_status==IPC_DISC_PENDING) { ch->ch_status =IPC_DISCONNECT; retcode = IPC_BROKEN; } break; case ECONNREFUSED: case ECONNRESET: ch->ch_status = IPC_DISC_PENDING; retcode= socket_check_disc_pending(ch); break; default: cl_perror("socket_resume_io_read" ": unknown recv error, peerpid=%d", ch->farside_pid); ch->ch_status = IPC_DISCONNECT; retcode = IPC_FAIL; break; } } else if (msg_len == 0) { ch->ch_status = IPC_DISC_PENDING; if(ch->recv_queue->current_qlen <= 0) { ch->ch_status = IPC_DISCONNECT; retcode = IPC_FAIL; } } else { /* We read something! */ /* Note that all previous cases break out of the loop */ debug_bytecount += msg_len; *nbytes = msg_len; nmsgs = ipc_bufpool_update(pool, ch, msg_len, ch->recv_queue) ; SocketIPCStats.ninqueued += nmsgs; } } /* Check for errors uncaught by recv() */ /* NOTE: It doesn't seem right we have to do this every time */ /* FIXME?? */ memset(&sockpoll,0, sizeof(struct pollfd)); if ((retcode == IPC_OK) && (sockpoll.fd = conn_info->s) >= 0) { /* Just check for errors, not for data */ sockpoll.events = 0; ipc_pollfunc_ptr(&sockpoll, 1, 0); retcode = socket_check_poll(ch, &sockpoll); } CHANAUDIT(ch); if (retcode != IPC_OK) { return retcode; } return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN; } static int socket_resume_io_write(struct IPC_CHANNEL *ch, int* nmsg) { int retcode = IPC_OK; struct SOCKET_CH_PRIVATE* conn_info; CHANAUDIT(ch); *nmsg = 0; conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; while (ch->ch_status == IPC_CONNECT && retcode == IPC_OK && ch->send_queue->current_qlen > 0) { GList * element; struct IPC_MESSAGE * msg; struct SOCKET_MSG_HEAD head; struct IPC_MESSAGE* oldmsg = NULL; int sendrc = 0; struct IPC_MESSAGE* newmsg; char* p; unsigned int bytes_remaining; int diff; CHANAUDIT(ch); element = g_list_first(ch->send_queue->queue); if (element == NULL) { /* OOPS! - correct consistency problem */ ch->send_queue->current_qlen = 0; break; } msg = (struct IPC_MESSAGE *) (element->data); diff = 0; if (msg->msg_buf ) { diff = (char*)msg->msg_body - (char*)msg->msg_buf; } if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) { /* either we don't have msg->msg_buf set * or we don't have enough bytes for socket head * we delete this message and creates * a new one and delete the old one */ newmsg= socket_message_new(ch, msg->msg_len); if (newmsg == NULL) { cl_log(LOG_ERR, "socket_resume_io_write: " "allocating memory for new ipc msg failed"); return IPC_FAIL; } memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len); oldmsg = msg; msg = newmsg; } head.msg_len = msg->msg_len; head.magic = HEADMAGIC; memcpy(msg->msg_buf, &head, sizeof(struct SOCKET_MSG_HEAD)); if (ch->bytes_remaining == 0) { /*we start to send a new message*/ #ifdef IPC_TIME_DEBUG ipc_time_debug(ch, msg, MSGPOS_SEND); #endif bytes_remaining = msg->msg_len + ch->msgpad; p = msg->msg_buf; } else { bytes_remaining = ch->bytes_remaining; p = ((char*)msg->msg_buf) + msg->msg_len + ch->msgpad - bytes_remaining; } sendrc = 0; do { #if HB_IPC_METHOD == HB_IPC_STREAM struct strbuf d; int msglen, putmsgrc; #endif CHANAUDIT(ch); #if HB_IPC_METHOD == HB_IPC_SOCKET sendrc = send(conn_info->s, p , bytes_remaining, (MSG_DONTWAIT|MSG_NOSIGNAL)); #elif HB_IPC_METHOD == HB_IPC_STREAM d.maxlen = 0; d.len = msglen = bytes_remaining; d.buf = p; putmsgrc = putmsg(conn_info->s, NULL, &d, 0); sendrc = putmsgrc == 0 ? msglen : -1; #endif SocketIPCStats.last_send_rc = sendrc; SocketIPCStats.last_send_errno = errno; ++SocketIPCStats.send_count; if (sendrc <= 0) { break; } else { p = p + sendrc; bytes_remaining -= sendrc; } } while(bytes_remaining > 0 ); ch->bytes_remaining = bytes_remaining; if (sendrc < 0) { switch (errno) { case EAGAIN: retcode = IPC_OK; break; case EPIPE: ch->ch_status = IPC_DISC_PENDING; socket_check_disc_pending(ch); retcode = IPC_BROKEN; break; default: cl_perror("socket_resume_io_write" ": send2 bad errno"); ch->ch_status = IPC_DISCONNECT; retcode = IPC_FAIL; break; } break; } else { int orig_qlen; CHECKFOO(3,ch, msg, SavedSentBody, "sent message") if (oldmsg) { if (msg->msg_done != NULL) { msg->msg_done(msg); } msg=oldmsg; } if(ch->bytes_remaining ==0) { ch->send_queue->queue = g_list_remove(ch->send_queue->queue, msg); if (msg->msg_done != NULL) { msg->msg_done(msg); } SocketIPCStats.nsent++; orig_qlen = ch->send_queue->current_qlen--; socket_check_flow_control(ch, orig_qlen, orig_qlen -1 ); (*nmsg)++; } } } CHANAUDIT(ch); if (retcode != IPC_OK) { return retcode; } return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN; } static int socket_resume_io(struct IPC_CHANNEL *ch) { int rc1 = IPC_OK; int rc2 = IPC_OK; int nwmsg = 1; int nbytes_r = 1; gboolean OKonce = FALSE; CHANAUDIT(ch); if (!IPC_ISRCONN(ch)) { return IPC_BROKEN; } do { if (nbytes_r > 0) { rc1 = socket_resume_io_read(ch, &nbytes_r, FALSE); } if (nwmsg > 0) { nwmsg = 0; rc2 = socket_resume_io_write(ch, &nwmsg); } if (rc1 == IPC_OK || rc2 == IPC_OK) { OKonce = TRUE; } } while ((nbytes_r > 0 || nwmsg > 0) && IPC_ISRCONN(ch)); if (IPC_ISRCONN(ch)) { if (rc1 != IPC_OK) { cl_log(LOG_ERR , "socket_resume_io_read() failure"); } if (rc2 != IPC_OK && IPC_CONNECT == ch->ch_status) { cl_log(LOG_ERR , "socket_resume_io_write() failure"); } } else { return (OKonce ? IPC_OK : IPC_BROKEN); } return (rc1 != IPC_OK ? rc1 : rc2); } static int socket_get_recv_fd(struct IPC_CHANNEL *ch) { struct SOCKET_CH_PRIVATE* chp = ch ->ch_private; return (chp == NULL ? -1 : chp->s); } static int socket_get_send_fd(struct IPC_CHANNEL *ch) { return socket_get_recv_fd(ch); } static void socket_adjust_buf(struct IPC_CHANNEL *ch, int optname, unsigned q_len) { const char *direction = optname == SO_SNDBUF ? "snd" : "rcv"; int fd = socket_get_send_fd(ch); unsigned byte; /* Arbitrary scaling. * DEFAULT_MAX_QLEN is 64, default socket buf is often 64k to 128k, * at least on those linux I checked. * Keep that ratio, and allow for some overhead. */ if (q_len == 0) /* client does not want anything, * reduce system buffers as well */ byte = 4096; else if (q_len < 512) byte = (32 + q_len) * 1024; else byte = q_len * 1024; if (0 == setsockopt(fd, SOL_SOCKET, optname, &byte, sizeof(byte))) { if (debug_level > 1) { cl_log(LOG_DEBUG, "adjusted %sbuf size to %u", direction, byte); } } else { /* If this fails, you may need to adjust net.core.rmem_max, * ...wmem_max, or equivalent */ cl_log(LOG_WARNING, "adjust %sbuf size to %u failed: %s", direction, byte, strerror(errno)); } } static int socket_set_send_qlen (struct IPC_CHANNEL* ch, int q_len) { /* This seems more like an assertion failure than a normal error */ if (ch->send_queue == NULL) { return IPC_FAIL; } socket_adjust_buf(ch, SO_SNDBUF, q_len); ch->send_queue->max_qlen = q_len; return IPC_OK; } static int socket_set_recv_qlen (struct IPC_CHANNEL* ch, int q_len) { /* This seems more like an assertion failure than a normal error */ if (ch->recv_queue == NULL) { return IPC_FAIL; } socket_adjust_buf(ch, SO_RCVBUF, q_len); ch->recv_queue->max_qlen = q_len; return IPC_OK; } static int ipcmsg_count_allocated = 0; static int ipcmsg_count_freed = 0; void socket_ipcmsg_dump_stats(void); void socket_ipcmsg_dump_stats(void) { cl_log(LOG_INFO, "ipcsocket ipcmsg allocated=%d, freed=%d, diff=%d", ipcmsg_count_allocated, ipcmsg_count_freed, ipcmsg_count_allocated - ipcmsg_count_freed); } static void socket_del_ipcmsg(IPC_Message* m) { if (m == NULL) { cl_log(LOG_ERR, "socket_del_ipcmsg:" "msg is NULL"); return; } if (m->msg_body) { memset(m->msg_body, 0, m->msg_len); } if (m->msg_buf) { g_free(m->msg_buf); } memset(m, 0, sizeof(*m)); g_free(m); ipcmsg_count_freed ++; } static IPC_Message* socket_new_ipcmsg(IPC_Channel* ch, const void* data, int len, void* private) { IPC_Message* hdr; if (ch == NULL || len < 0) { cl_log(LOG_ERR, "socket_new_ipcmsg:" " invalid parameter"); return NULL; } if (ch->msgpad > MAX_MSGPAD) { cl_log(LOG_ERR, "socket_new_ipcmsg: too many pads " "something is wrong"); return NULL; } hdr = ipcmsg_new(ch, data, len, private, socket_del_ipcmsg); if (hdr) ipcmsg_count_allocated ++; return hdr; } static struct IPC_MESSAGE * ipcmsg_new(struct IPC_CHANNEL * ch, const void* data, int len, void* private, DelProc delproc) { struct IPC_MESSAGE * hdr; char* copy = NULL; char* buf; char* body; if ((hdr = g_new(struct IPC_MESSAGE, 1)) == NULL) { return NULL; } memset(hdr, 0, sizeof(*hdr)); if (len > 0) { if ((copy = (char*)g_malloc(ch->msgpad + len)) == NULL) { g_free(hdr); return NULL; } if (data) { memcpy(copy + ch->msgpad, data, len); } buf = copy; body = copy + ch->msgpad;; } else { len = 0; buf = body = NULL; } hdr->msg_len = len; hdr->msg_buf = buf; hdr->msg_body = body; hdr->msg_ch = ch; hdr->msg_done = delproc; hdr->msg_private = private; return hdr; } static int socket_get_chan_status(IPC_Channel* ch) { socket_resume_io(ch); return ch->ch_status; } /* socket object of the function table */ static struct IPC_WAIT_OPS socket_wait_ops = { socket_destroy_wait_conn, socket_wait_selectfd, socket_accept_connection, }; /* * create a new ipc queue whose length = 0 and inner queue = NULL. * return the pointer to a new ipc queue or NULL is the queue can't be created. */ static struct IPC_QUEUE* socket_queue_new(void) { struct IPC_QUEUE *temp_queue; /* temp queue with length = 0 and inner queue = NULL. */ temp_queue = g_new(struct IPC_QUEUE, 1); temp_queue->current_qlen = 0; temp_queue->max_qlen = DEFAULT_MAX_QLEN; temp_queue->queue = NULL; temp_queue->last_maxqlen_warn = 0; temp_queue->maxqlen_cnt = 0; return temp_queue; } /* * socket_wait_conn_new: * Called by ipc_wait_conn_constructor to get a new socket * waiting connection. * (better explanation of this role might be nice) * * Parameters : * ch_attrs (IN) the attributes used to create this connection. * * Return : * the pointer to the new waiting connection or NULL if the connection * can't be created. * * NOTE : * for domain socket implementation, the only attribute needed is path name. * so the user should * create the hash table like this: * GHashTable * attrs; * attrs = g_hash_table_new(g_str_hash, g_str_equal); * g_hash_table_insert(attrs, PATH_ATTR, path_name); * here PATH_ATTR is defined as "path". * * NOTE : * The streams implementation uses "Streams Programming Guide", Solaris 8, * as its guide (sample code near end of "Configuration" chapter 11). */ struct IPC_WAIT_CONNECTION * socket_wait_conn_new(GHashTable *ch_attrs) { struct IPC_WAIT_CONNECTION * temp_ch; char *path_name; char *mode_attr; int s, flags; struct SOCKET_WAIT_CONN_PRIVATE *wait_private; mode_t s_mode; #if HB_IPC_METHOD == HB_IPC_SOCKET struct sockaddr_un my_addr; #elif HB_IPC_METHOD == HB_IPC_STREAM int pipefds[2]; #endif path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR); mode_attr = (char *) g_hash_table_lookup(ch_attrs, IPC_MODE_ATTR); if (mode_attr != NULL) { s_mode = (mode_t)strtoul((const char *)mode_attr, NULL, 8); } else { s_mode = 0777; } if (path_name == NULL) { return NULL; } #if HB_IPC_METHOD == HB_IPC_SOCKET /* prepare the unix domain socket */ if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { cl_perror("socket_wait_conn_new: socket() failure"); return NULL; } if (unlink(path_name) < 0 && errno != ENOENT) { cl_perror("socket_wait_conn_new: unlink failure(%s)", path_name); } memset(&my_addr, 0, sizeof(my_addr)); my_addr.sun_family = AF_LOCAL; /* host byte order */ if (strlen(path_name) >= sizeof(my_addr.sun_path)) { close(s); return NULL; } strncpy(my_addr.sun_path, path_name, sizeof(my_addr.sun_path)); if (bind(s, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) { cl_perror("socket_wait_conn_new: trying to create in %s bind:" , path_name); close(s); return NULL; } #elif HB_IPC_METHOD == HB_IPC_STREAM /* Set up the communication channel the clients will use to us (server) */ if (pipe(pipefds) == -1) { cl_perror("pipe() failure"); return NULL; } /* Let clients have unique connections to us */ if (ioctl(pipefds[1], I_PUSH, "connld") == -1) { cl_perror("ioctl(%d, I_PUSH, \"connld\") failure", pipefds[1]); return NULL; } if (unlink(path_name) < 0 && errno != ENOENT) { cl_perror("socket_wait_conn_new: unlink failure(%s)", path_name); } if (mkfifo(path_name, s_mode) == -1) { cl_perror("socket_wait_conn_new: mkfifo(%s, ...) failure", path_name); return NULL; } if (fattach(pipefds[1], path_name) == -1) { cl_perror("socket_wait_conn_new: fattach(..., %s) failure", path_name); return NULL; } /* the pseudo-socket is the other part of the pipe */ s = pipefds[0]; #endif /* Change the permission of the socket */ if (chmod(path_name,s_mode) < 0) { cl_perror("socket_wait_conn_new: failure trying to chmod %s" , path_name); close(s); return NULL; } #if HB_IPC_METHOD == HB_IPC_SOCKET /* listen to the socket */ if (listen(s, MAX_LISTEN_NUM) == -1) { cl_perror("socket_wait_conn_new: listen(MAX_LISTEN_NUM)"); close(s); return NULL; } #elif HB_IPC_METHOD == HB_IPC_STREAM #endif flags = fcntl(s, F_GETFL); if (flags == -1) { cl_perror("socket_wait_conn_new: cannot read file descriptor flags"); close(s); return NULL; } flags |= O_NONBLOCK; if (fcntl(s, F_SETFL, flags) < 0) { cl_perror("socket_wait_conn_new: cannot set O_NONBLOCK"); close(s); return NULL; } wait_private = g_new(struct SOCKET_WAIT_CONN_PRIVATE, 1); #if HB_IPC_METHOD == HB_IPC_SOCKET wait_private->s = s; #elif HB_IPC_METHOD == HB_IPC_STREAM wait_private->pipefds[0] = pipefds[0]; wait_private->pipefds[1] = pipefds[1]; #endif strncpy(wait_private->path_name, path_name, sizeof(wait_private->path_name)); temp_ch = g_new(struct IPC_WAIT_CONNECTION, 1); temp_ch->ch_private = (void *) wait_private; temp_ch->ch_status = IPC_WAIT; temp_ch->ops = (struct IPC_WAIT_OPS *)&socket_wait_ops; return temp_ch; } /* * will be called by ipc_channel_constructor to create a new socket channel. * parameters : * attrs (IN) the hash table of the attributes used to create this channel. * * return: * the pointer to the new waiting channel or NULL if the channel can't be created. */ struct IPC_CHANNEL * socket_client_channel_new(GHashTable *ch_attrs) { char *path_name; int sockfd; /* * I don't really understand why the client and the server use different * parameter names... * * It's a really bad idea to store both integers and strings * in the same table. * * Maybe we need an internal function with a different set of parameters? */ /* * if we want to seperate them. I suggest * * user call ipc_channel_constructor(ch_type,attrs) to create a new channel. * ipc_channel_constructor() call socket_channel_new(GHashTable*)to * create a new socket channel. * * wait_conn->accept_connection() will call another function to create a * new channel. This function will take socketfd as the parameter to * create a socket channel. */ path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR); if (path_name == NULL) { return NULL; } #if HB_IPC_METHOD == HB_IPC_SOCKET /* prepare the socket */ if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { cl_perror("socket_client_channel_new: socket"); return NULL; } #elif HB_IPC_METHOD == HB_IPC_STREAM sockfd = open(path_name, O_RDWR|O_NONBLOCK); if (sockfd == -1) { cl_perror("socket_client_channel_new: open(%s, ...) failure", path_name); return NULL; } #endif if (client_channel_new_auth(sockfd) < 0) { close(sockfd); return NULL; } return channel_new(sockfd, IPC_CLIENT, path_name); } static int client_channel_new_auth(int sockfd) { #ifdef USE_BINDSTAT_CREDS char rand_id[16]; char uuid_str_tmp[40]; struct sockaddr_un sock_addr; /* Prepare the socket */ memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sun_family = AF_UNIX; /* make sure socket paths never clash */ uuid_generate(rand_id); uuid_unparse(rand_id, uuid_str_tmp); snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path), "%s/%s", HA_VARLIBHBDIR, uuid_str_tmp); unlink(sock_addr.sun_path); if(bind(sockfd, (struct sockaddr*)&sock_addr, SUN_LEN(&sock_addr)) < 0) { perror("Client bind() failure"); return 0; } #endif return 0; } static struct IPC_CHANNEL * socket_server_channel_new(int sockfd) { return channel_new(sockfd, IPC_SERVER, "?"); } static struct IPC_CHANNEL * channel_new(int sockfd, int conntype, const char *path_name) { struct IPC_CHANNEL * temp_ch; struct SOCKET_CH_PRIVATE* conn_info; int flags; if (path_name == NULL || strlen(path_name) >= sizeof(conn_info->path_name)) { return NULL; } temp_ch = g_new(struct IPC_CHANNEL, 1); if (temp_ch == NULL) { cl_log(LOG_ERR, "channel_new: allocating memory for channel failed"); return NULL; } memset(temp_ch, 0, sizeof(struct IPC_CHANNEL)); conn_info = g_new(struct SOCKET_CH_PRIVATE, 1); flags = fcntl(sockfd, F_GETFL); if (flags == -1) { cl_perror("channel_new: cannot read file descriptor flags"); g_free(conn_info); conn_info = NULL; g_free(temp_ch); if (conntype == IPC_CLIENT) close(sockfd); return NULL; } flags |= O_NONBLOCK; if (fcntl(sockfd, F_SETFL, flags) < 0) { cl_perror("channel_new: cannot set O_NONBLOCK"); g_free(conn_info); conn_info = NULL; g_free(temp_ch); if (conntype == IPC_CLIENT) close(sockfd); return NULL; } conn_info->s = sockfd; conn_info->remaining_data = 0; conn_info->buf_msg = NULL; #if HB_IPC_METHOD == HB_IPC_SOCKET conn_info->peer_addr = NULL; #endif strncpy(conn_info->path_name, path_name, sizeof(conn_info->path_name)); #ifdef DEBUG cl_log(LOG_INFO, "Initializing socket %d to DISCONNECT", sockfd); #endif temp_ch->ch_status = IPC_DISCONNECT; temp_ch->ch_private = (void*) conn_info; temp_ch->ops = (struct IPC_OPS *)&socket_ops; temp_ch->msgpad = sizeof(struct SOCKET_MSG_HEAD); temp_ch->bytes_remaining = 0; temp_ch->should_send_block = FALSE; temp_ch->should_block_fail = TRUE; temp_ch->send_queue = socket_queue_new(); temp_ch->recv_queue = socket_queue_new(); temp_ch->pool = NULL; temp_ch->high_flow_mark = temp_ch->send_queue->max_qlen; temp_ch->low_flow_mark = -1; temp_ch->conntype = conntype; temp_ch->refcount = 0; temp_ch->farside_uid = -1; temp_ch->farside_gid = -1; return temp_ch; } /* * Create a new pair of pre-connected IPC channels similar to * the result of pipe(2), or socketpair(2). */ int ipc_channel_pair(IPC_Channel* channels[2]) { int sockets[2]; int rc; int j; const char *pname; #if HB_IPC_METHOD == HB_IPC_SOCKET pname = "[socketpair]"; if ((rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets)) < 0) { return IPC_FAIL; } #elif HB_IPC_METHOD == HB_IPC_STREAM pname = "[pipe]"; if ((rc = pipe(sockets)) < 0) { return IPC_FAIL; } rc = 0; for (j=0; j < 2; ++j) { if (fcntl(sockets[j], F_SETFL, O_NONBLOCK) < 0) { cl_perror("ipc_channel_pair: cannot set O_NONBLOCK"); rc = -1; } } if (rc < 0) { close(sockets[0]); close(sockets[1]); return IPC_FAIL; } #endif if ((channels[0] = socket_server_channel_new(sockets[0])) == NULL) { close(sockets[0]); close(sockets[1]); return IPC_FAIL; } if ((channels[1] = socket_server_channel_new(sockets[1])) == NULL) { close(sockets[0]); close(sockets[1]); channels[0]->ops->destroy(channels[0]); return IPC_FAIL; } for (j=0; j < 2; ++j) { struct SOCKET_CH_PRIVATE* p = channels[j]->ch_private; channels[j]->ch_status = IPC_CONNECT; channels[j]->conntype = IPC_PEER; /* Valid, but not terribly meaningful */ channels[j]->farside_pid = getpid(); strncpy(p->path_name, pname, sizeof(p->path_name)); } return IPC_OK; } /* brief free the memory space allocated to msg and destroy msg. */ static void socket_free_message(struct IPC_MESSAGE * msg) { #if 0 memset(msg->msg_body, 0xff, msg->msg_len); #endif if (msg->msg_buf) { g_free(msg->msg_buf); } else { g_free(msg->msg_body); } #if 0 memset(msg, 0xff, sizeof(*msg)); #endif g_free((void *)msg); } /* * create a new ipc message whose msg_body's length is msg_len. * * parameters : * msg_len (IN) the length of this message body in this message. * * return : * the pointer to the new message or NULL if the message can't be created. */ static struct IPC_MESSAGE* socket_message_new(struct IPC_CHANNEL *ch, int msg_len) { return ipcmsg_new(ch, NULL, msg_len, NULL, socket_free_message); } /*********************************************************************** * * IPC authentication schemes... More machine dependent than * we'd like, but don't know any better way... * ***********************************************************************/ static int verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid) { int ret = IPC_FAIL; if (!auth_info || (!auth_info->uid && !auth_info->gid)) { return IPC_OK; } if ( auth_info->uid && (g_hash_table_lookup(auth_info->uid , GUINT_TO_POINTER((guint)uid)) != NULL)) { ret = IPC_OK; } else if (auth_info->gid && (g_hash_table_lookup(auth_info->gid , GUINT_TO_POINTER((guint)gid)) != NULL)) { ret = IPC_OK; } return ret; } /*********************************************************************** * SO_PEERCRED VERSION... (Linux) ***********************************************************************/ #ifdef USE_SO_PEERCRED /* verify the authentication information. */ static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { struct SOCKET_CH_PRIVATE * conn_info; int ret = IPC_FAIL; struct ucred cred; socklen_t n = sizeof(cred); if (ch == NULL || ch->ch_private == NULL) { return IPC_FAIL; } if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { ret = IPC_OK; /* no restriction for authentication */ } /* Get the credential information for our peer */ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (getsockopt(conn_info->s, SOL_SOCKET, SO_PEERCRED, &cred, &n) != 0 || (size_t)n != sizeof(cred)) { return ret; } ch->farside_uid = cred.uid; ch->farside_gid = cred.gid; if (ret == IPC_OK) { return ret; } #if 0 cl_log(LOG_DEBUG, "SO_PEERCRED returned [%d, (%ld:%ld)]" , cred.pid, (long)cred.uid, (long)cred.uid); cl_log(LOG_DEBUG, "Verifying authentication: cred.uid=%d cred.gid=%d" , cred.uid, cred.gid); cl_log(LOG_DEBUG, "Verifying authentication: uidptr=0x%lx gidptr=0x%lx" , (unsigned long)auth_info->uid , (unsigned long)auth_info->gid); #endif /* verify the credential information. */ return verify_creds(auth_info, cred.uid, cred.gid); } /* get farside pid for our peer process */ static pid_t socket_get_farside_pid(int sockfd) { socklen_t n; struct ucred *cred; pid_t f_pid; /* Get the credential information from peer */ n = sizeof(struct ucred); cred = g_new(struct ucred, 1); if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, cred, &n) != 0) { g_free(cred); return -1; } f_pid = cred->pid; g_free(cred); return f_pid; } #endif /* SO_PEERCRED version */ #ifdef USE_GETPEEREID /* * This is implemented in OpenBSD and FreeBSD. * * It's not a half-bad interface... * * This should probably be our standard way of doing it, and put it * as a replacement library. That would simplify things... */ static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { struct SOCKET_CH_PRIVATE *conn_info; uid_t euid; gid_t egid; int ret = IPC_FAIL; if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { ret = IPC_OK; /* no restriction for authentication */ } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (getpeereid(conn_info->s, &euid, &egid) < 0) { cl_perror("getpeereid() failure"); return ret; } ch->farside_uid = euid; ch->farside_gid = egid; /* verify the credential information. */ return verify_creds(auth_info, euid, egid); } static pid_t socket_get_farside_pid(int sock) { return -1; } #endif /* USE_GETPEEREID */ /*********************************************************************** * SCM_CREDS VERSION... (*BSD systems) ***********************************************************************/ #ifdef USE_SCM_CREDS /* FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems * This isn't an emergency, but should be done in the future... * Hint: * Postgresql does both types of authentication... * see src/backend/libpq/auth.c * Not clear its SO_PEERCRED implementation works though ;-) */ /* Done.... Haven't tested yet. */ static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { struct msghdr msg; /* Credentials structure */ #define EXTRASPACE 0 #ifdef HAVE_STRUCT_CMSGCRED /* FreeBSD */ typedef struct cmsgcred Cred; # define crRuid cmcred_uid # define crEuid cmcred_euid # define crRgid cmcred_gid # define crEgid cmcred_groups[0] /* Best guess */ # define crpid cmcred_pid # define crngrp cmcred_ngroups # define crgrps cmcred_groups #elif HAVE_STRUCT_FCRED /* Stevens' book */ typedef struct fcred Cred; # define crRuid fc_uid # define crRgid fc_rgid # define crEgid fc_gid # define crngrp fc_ngroups # define crgrps fc_groups #elif HAVE_STRUCT_SOCKCRED /* NetBSD */ typedef struct sockcred Cred; # define crRuid sc_uid # define crEuid sc_euid # define crRgid sc_gid # define crEgid sc_egid # define crngrp sc_ngroups # define crgrps sc_groups # undef EXTRASPACE # define EXTRASPACE SOCKCREDSIZE(ngroups) #elif HAVE_STRUCT_CRED typedef struct cred Cred; #define cruid c_uid #elif HAVE_STRUCT_UCRED typedef struct ucred Cred; /* reuse this define for the moment */ # if HAVE_STRUCT_UCRED_DARWIN # define crEuid cr_uid # define crEgid cr_groups[0] /* Best guess */ # define crgrps cr_groups # define crngrp cr_ngroups # else # define crEuid c_uid # define crEgid c_gid # endif #else # error "No credential type found!" #endif struct SOCKET_CH_PRIVATE *conn_info; int ret = IPC_FAIL; char buf; /* Compute size without padding */ #define CMSGSIZE (sizeof(struct cmsghdr)+(sizeof(Cred))+EXTRASPACE) union { char mem[CMSGSIZE]; struct cmsghdr hdr; Cred credu; }cmsgmem; Cred cred; /* Point to start of first structure */ struct cmsghdr *cmsg = &cmsgmem.hdr; if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { ret = IPC_OK; /* no restriction for authentication */ } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; memset(&msg, 0, sizeof(msg)); msg.msg_iov = g_new(struct iovec, 1); msg.msg_iovlen = 1; msg.msg_control = (char *) cmsg; msg.msg_controllen = CMSGSIZE; memset(cmsg, 0, sizeof(cmsgmem)); /* * The one character which is received here is not meaningful; its * purpose is only to make sure that recvmsg() blocks long enough for * the other side to send its credentials. */ msg.msg_iov->iov_base = &buf; msg.msg_iov->iov_len = 1; if (recvmsg(conn_info->s, &msg, 0) < 0 || cmsg->cmsg_len < CMSGSIZE || cmsg->cmsg_type != SCM_CREDS) { cl_perror("can't get credential information from peer"); return ret; } /* Avoid alignment issues - just copy it! */ memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); ch->farside_uid = cred.crEuid; ch->farside_gid = cred.crEgid; if (ret == IPC_OK) { return ret; } /* verify the credential information. */ return verify_creds(auth_info, cred.crEuid, cred.crEgid); } /* * FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems * this is similar to the SCM_CREDS mechanism for verify_auth() function. * here we just want to get the pid of the other side from the credential * information. */ static pid_t socket_get_farside_pid(int sock) { /* FIXME! */ return -1; } #endif /* SCM_CREDS version */ /*********************************************************************** * Bind/Stat VERSION... (Supported on OSX/Darwin and 4.3+BSD at least...) * * This is for use on systems such as OSX-Darwin where * none of the other options is available. * * This implementation has been adapted from "Advanced Programming * in the Unix Environment", Section 15.5.2, by W. Richard Stevens. * */ #ifdef USE_BINDSTAT_CREDS static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { int len = 0; int ret = IPC_FAIL; struct stat stat_buf; struct sockaddr_un *peer_addr = NULL; struct SOCKET_CH_PRIVATE *ch_private = NULL; if(ch != NULL) { ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private); if(ch_private != NULL) { peer_addr = ch_private->peer_addr; } } if(ch == NULL) { cl_log(LOG_ERR, "No channel to authenticate"); return IPC_FAIL; } else if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { ret = IPC_OK; /* no restriction for authentication */ } if(ch_private == NULL) { cl_log(LOG_ERR, "No channel private data available"); return ret; } else if(peer_addr == NULL) { cl_log(LOG_ERR, "No peer information available"); return ret; } len = SUN_LEN(peer_addr); if(len < 1) { cl_log(LOG_ERR, "No peer information available"); return ret; } peer_addr->sun_path[len] = 0; stat(peer_addr->sun_path, &stat_buf); ch->farside_uid = stat_buf.st_uid; ch->farside_gid = stat_buf.st_gid; if (ret == IPC_OK) { return ret; } if ((auth_info->uid == NULL || g_hash_table_size(auth_info->uid) == 0) && auth_info->gid != NULL && g_hash_table_size(auth_info->gid) != 0) { cl_log(LOG_WARNING, "GID-Only IPC security is not supported" " on this platform."); return IPC_BROKEN; } /* verify the credential information. */ return verify_creds(auth_info, stat_buf.st_uid, stat_buf.st_gid); } static pid_t socket_get_farside_pid(int sock) { return -1; } #endif /* Bind/stat version */ /*********************************************************************** * USE_STREAM_CREDS VERSION... (e.g. Solaris pre-10) ***********************************************************************/ #ifdef USE_STREAM_CREDS static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { struct SOCKET_CH_PRIVATE *conn_info; if (ch == NULL || ch->ch_private == NULL) { return IPC_FAIL; } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; ch->farside_uid = conn_info->farside_uid; ch->farside_gid = conn_info->farside_gid; /* verify the credential information. */ return verify_creds(auth_info, conn_info->farside_uid, conn_info->farside_gid); } static pid_t socket_get_farside_pid(int sock) { return -1; } #endif /*********************************************************************** * GETPEERUCRED VERSION... (e.g. Solaris 10 upwards) ***********************************************************************/ #ifdef USE_GETPEERUCRED /* verify the authentication information. */ static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { struct SOCKET_CH_PRIVATE *conn_info; ucred_t *ucred = NULL; int rc = IPC_FAIL; if (ch == NULL || ch->ch_private == NULL) { return IPC_FAIL; } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { rc = IPC_OK; /* no restriction for authentication */ } if (getpeerucred(conn_info->s, &ucred) < 0) { cl_perror("getpeereid() failure"); return rc; } ch->farside_uid = ucred_geteuid(ucred); ch->farside_gid = ucred_getegid(ucred); if (rc == IPC_OK) { return rc; } /* verify the credential information. */ rc = verify_creds(auth_info, ucred_geteuid(ucred), ucred_getegid(ucred)); ucred_free(ucred); return rc; } static pid_t socket_get_farside_pid(int sockfd) { ucred_t *ucred = NULL; pid_t pid; if (getpeerucred(sockfd, &ucred) < 0) { cl_perror("getpeereid() failure"); return IPC_FAIL; } pid = ucred_getpid(ucred); ucred_free(ucred); return pid; } #endif /*********************************************************************** * DUMMY VERSION... (other systems...) * * Other options that seem to be out there include * SCM_CREDENTIALS and LOCAL_CREDS * There are some kludgy things you can do with SCM_RIGHTS * to pass an fd which could only be opened by the user id to * validate the user id, but I don't know of a similar kludge which * would work for group ids. And, even the uid one will fail * if normal users are allowed to give away (chown) files. * * Unfortunately, this set of authentication routines have become * very important to this API and its users (like heartbeat). * ***********************************************************************/ #ifdef USE_DUMMY_CREDS static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info) { return IPC_FAIL; } static pid_t socket_get_farside_pid(int sock) { return -1; } #endif /* Dummy version */ /* socket object of the function table */ static struct IPC_OPS socket_ops = { socket_destroy_channel, socket_initiate_connection, socket_verify_auth, socket_assert_auth, socket_send, socket_recv, socket_waitin, socket_waitout, socket_is_message_pending, socket_is_output_pending, socket_resume_io, socket_get_send_fd, socket_get_recv_fd, socket_set_send_qlen, socket_set_recv_qlen, socket_set_high_flow_callback, socket_set_low_flow_callback, socket_new_ipcmsg, socket_get_chan_status, socket_is_sendq_full, socket_is_recvq_full, socket_get_conntype, socket_disconnect, }; Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipctest.c0000644000000000000000000007537512120057602026023 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #undef _GNU_SOURCE /* in case it was defined on the command line */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include /* libgen.h: for 'basename()' on Solaris */ #include #include #include #include #include #include #define MAXERRORS 1000 #define MAXERRORS_RECV 10 typedef int (*TestFunc_t)(IPC_Channel*chan, int count); static int channelpair(TestFunc_t client, TestFunc_t server, int count); #if 0 static void clientserverpair(IPC_Channel* channels[2]); #endif static int echoserver(IPC_Channel*, int repcount); static int echoclient(IPC_Channel*, int repcount); static int asyn_echoserver(IPC_Channel*, int repcount); static int asyn_echoclient(IPC_Channel*, int repcount); static int mainloop_server(IPC_Channel* chan, int repcount); static int mainloop_client(IPC_Channel* chan, int repcount); static int checksock(IPC_Channel* channel); static void checkifblocked(IPC_Channel* channel); static int (*PollFunc)(struct pollfd * fds, unsigned int, int) = (int (*)(struct pollfd * fds, unsigned int, int)) poll; static gboolean checkmsg(IPC_Message* rmsg, const char * who, int rcount); static const char *procname; static const int iter_def = 10000; /* number of iterations */ static int verbosity; /* verbosity level */ /* * The ipc interface can be invoked as either: * 1. pair (pipe/socketpair); * 2. separate connect/accept (like server with multiple independent clients). * * If number of clients is given as 0, the "pair" mechanism is used, * otherwise the client/server mechanism. */ /* *** CLIENTS_MAX currently 1 while coding *** */ #define CLIENTS_MAX 1 /* max. number of independent clients */ static int clients_def; /* number of independent clients */ static int channelpair(TestFunc_t clientfunc, TestFunc_t serverfunc, int count) { IPC_Channel* channels[2]; int rc = 0; int waitstat = 0; if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main process", procname, (int)getpid(), __LINE__); } switch (fork()) { case -1: cl_perror("can't fork"); exit(1); break; default: /* Parent */ if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...", procname, (int)getpid(), __LINE__); } while (wait(&waitstat) > 0) { if (WIFEXITED(waitstat)) { rc += WEXITSTATUS(waitstat); }else{ rc += 1; } } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d", procname, (int)getpid(), __LINE__, rc); } if (rc > 127) { rc = 127; } exit(rc); break; case 0: /* Child */ break; } /* Child continues here... */ if (ipc_channel_pair(channels) != IPC_OK) { cl_perror("Can't create ipc channel pair"); exit(1); } checksock(channels[0]); checksock(channels[1]); switch (fork()) { case -1: cl_perror("can't fork"); exit(1); break; case 0: /* echo "client" Child */ channels[1]->ops->destroy(channels[1]); channels[1] = NULL; if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: client starting...", procname, (int)getpid(), __LINE__); } rc = clientfunc(channels[0], count); if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: client ended rc:%d", procname, (int)getpid(), __LINE__, rc); } exit (rc > 127 ? 127 : rc); break; default: break; } channels[0]->ops->destroy(channels[0]); channels[0] = NULL; if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: server starting...", procname, (int)getpid(), __LINE__); } rc = serverfunc(channels[1], count); wait(&waitstat); if (WIFEXITED(waitstat)) { rc += WEXITSTATUS(waitstat); }else{ rc += 1; } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d", procname, (int)getpid(), __LINE__, rc); } return(rc); } /* server with many clients */ static int clientserver(TestFunc_t clientfunc, TestFunc_t serverfunc, int count, int clients) { IPC_Channel* channel; int rc = 0; int waitstat = 0; struct IPC_WAIT_CONNECTION *wconn; char path[] = IPC_PATH_ATTR; char commpath[] = "/tmp/foobar"; /* *** CHECK/FIX: Is this OK? */ GHashTable * wattrs; int i; pid_t pid; if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main process", procname, (int)getpid(), __LINE__); } switch (fork()) { case -1: cl_perror("can't fork"); exit(1); break; default: /* Parent */ if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...", procname, (int)getpid(), __LINE__); } while ((pid = wait(&waitstat)) > 0) { if (WIFEXITED(waitstat)) { rc += WEXITSTATUS(waitstat); }else{ rc += 1; } } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d", procname, (int)getpid(), __LINE__, rc); } if (rc > 127) { rc = 127; } exit(rc); break; case 0: /* Child */ break; } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d:", procname, (int)getpid(), __LINE__); } /* set up a server */ wattrs = g_hash_table_new(g_str_hash, g_str_equal); if (! wattrs) { cl_perror("g_hash_table_new() failed"); exit(1); } g_hash_table_insert(wattrs, path, commpath); if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d:", procname, (int)getpid(), __LINE__); } wconn = ipc_wait_conn_constructor(IPC_ANYTYPE, wattrs); if (! wconn) { cl_perror("could not establish server"); exit(1); } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d:", procname, (int)getpid(), __LINE__); } /* spawn the clients */ for (i = 1; i <= clients; i++) { if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: fork client %d of %d", procname, (int)getpid(), __LINE__, i, clients); } switch (fork()) { case -1: cl_perror("can't fork"); exit(1); break; case 0: /* echo "client" Child */ if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: client %d starting...", procname, (int)getpid(), __LINE__, i); } channel = ipc_channel_constructor(IPC_ANYTYPE, wattrs); if (channel == NULL) { cl_perror("client: channel creation failed"); exit(1); } rc = channel->ops->initiate_connection(channel); if (rc != IPC_OK) { cl_perror("channel[1] failed to connect"); exit(1); } checksock(channel); rc = clientfunc(channel, count); if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: client %d ended rc:%d", procname, (int)getpid(), __LINE__, rc, i); } exit (rc > 127 ? 127 : rc); break; default: break; } } if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: server starting...", procname, (int)getpid(), __LINE__); } /* accept on server */ /* *** * Two problems (or more) here: * 1. What to do if no incoming call pending? * At present, fudge by sleeping a little so client gets started. * 2. How to handle multiple clients? * Would need to be able to await both new connections and * data on existing connections. * At present, fudge CLIENTS_MAX as 1. * *** */ sleep(1); /* *** */ channel = wconn->ops->accept_connection(wconn, NULL); if (channel == NULL) { cl_perror("server: acceptance failed"); } checksock(channel); rc = serverfunc(channel, count); /* server finished: tidy up */ wconn->ops->destroy(wconn); if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d", procname, (int)getpid(), __LINE__, rc); } /* reap the clients */ for (i = 1; i <= clients; i++) { pid_t pid; pid = wait(&waitstat); if (verbosity >= 1) { cl_log(LOG_DEBUG, "%s[%d]%d: client %d reaped:%d", procname, (int)getpid(), __LINE__, (int) pid, WIFEXITED(waitstat)); } if (WIFEXITED(waitstat)) { rc += WEXITSTATUS(waitstat); }else{ rc += 1; } } return(rc); } static void echomsgbody(void * body, int n, int niter, size_t * len) { char *str = body; int l; l = snprintf(str, n-1, "String-%d", niter); if (l < (n-1)) { memset(&str[l], 'a', (n - (l+1))); } str[n-1] = '\0'; *len = n; } static void checkifblocked(IPC_Channel* chan) { if (chan->ops->is_sending_blocked(chan)) { cl_log(LOG_INFO, "Sending is blocked."); chan->ops->resume_io(chan); } } #ifdef CHEAT_CHECKS extern long SeqNums[32]; #endif static int transport_tests(int iterations, int clients) { int rc = 0; #ifdef CHEAT_CHECKS memset(SeqNums, 0, sizeof(SeqNums)); #endif rc += (clients <= 0) ? channelpair(echoclient, echoserver, iterations) : clientserver(echoclient, echoserver, iterations, clients); #ifdef CHEAT_CHECKS memset(SeqNums, 0, sizeof(SeqNums)); #endif rc += (clients <= 0) ? channelpair(asyn_echoclient, asyn_echoserver, iterations) : clientserver(asyn_echoclient, asyn_echoserver, iterations, clients); #ifdef CHEAT_CHECKS memset(SeqNums, 0, sizeof(SeqNums)); #endif rc += (clients <= 0) ? channelpair(mainloop_client, mainloop_server, iterations) : clientserver(mainloop_client, mainloop_server, iterations, clients); return rc; } static int data_size = 20; int main(int argc, char ** argv) { int argflag, argerrs; int iterations; int clients; int rc = 0; /* * Check and process arguments. * -v: verbose * -i: number of iterations * -c: number of clients (invokes client/server mechanism) * -s: data-size */ procname = basename(argv[0]); argerrs = 0; iterations = iter_def; clients = clients_def; while ((argflag = getopt(argc, argv, "i:vuc:s:")) != EOF) { switch (argflag) { case 'i': /* iterations */ iterations = atoi(optarg); break; case 'v': /* verbosity */ verbosity++; break; case 'c': /* number of clients */ clients = atoi(optarg); if (clients < 1 || clients > CLIENTS_MAX) { fprintf(stderr, "number of clients out of range" "(1 to %d)\n", CLIENTS_MAX); argerrs++; } break; case 's': /* data size */ data_size = atoi(optarg); if (data_size < 0) { fprintf(stderr, "data size must be >=0\n"); argerrs++; } if (data_size > MAXMSG) { fprintf(stderr, "maximum data size is %d\n", MAXMSG); argerrs++; } break; default: argerrs++; break; } } if (argerrs) { fprintf(stderr, "Usage: %s [-v] [-i iterations] [-c clients] [-s size]\n" "\t-v : verbose\n" "\t-i : iterations (default %d)\n" "\t-c : number of clients (default %d; nonzero invokes client/server)\n" "\t-s : data size (default 20 bytes)\n", procname, iter_def, clients_def); exit(1); } cl_log_set_entity(procname); cl_log_enable_stderr(TRUE); rc += transport_tests(iterations, clients); #if 0 /* Broken for the moment - need to fix it long term */ cl_log(LOG_INFO, "NOTE: Enabling poll(2) replacement code."); PollFunc = cl_poll; g_main_set_poll_func(cl_glibpoll); ipc_set_pollfunc(cl_poll); rc += transport_tests(5 * iterations, clients); #endif cl_log(LOG_INFO, "TOTAL errors: %d", rc); return (rc > 127 ? 127 : rc); } static int checksock(IPC_Channel* channel) { if (!channel) { cl_log(LOG_ERR, "Channel null"); return 1; } if (!IPC_ISRCONN(channel)) { cl_log(LOG_ERR, "Channel status is %d" ", not IPC_CONNECT", channel->ch_status); return 1; } return 0; } static void EOFcheck(IPC_Channel* chan) { int fd = chan->ops->get_recv_select_fd(chan); struct pollfd pf[1]; int rc; cl_log(LOG_INFO, "channel state: %d", chan->ch_status); if (chan->recv_queue->current_qlen > 0) { cl_log(LOG_INFO, "EOF Receive queue has %ld messages in it" , (long)chan->recv_queue->current_qlen); } if (fd <= 0) { cl_log(LOG_INFO, "EOF receive fd: %d", fd); } pf[0].fd = fd; pf[0].events = POLLIN|POLLHUP; pf[0].revents = 0; rc = poll(pf, 1, 0); if (rc < 0) { cl_perror("failed poll(2) call in EOFcheck"); return; } /* Got input? */ if (pf[0].revents & POLLIN) { cl_log(LOG_INFO, "EOF socket %d (still) has input ready (real poll)" , fd); } if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) { cl_log(LOG_INFO, "EOFcheck poll(2) bits: 0x%lx" , (unsigned long)pf[0].revents); } pf[0].fd = fd; pf[0].events = POLLIN|POLLHUP; pf[0].revents = 0; rc = PollFunc(pf, 1, 0); if (rc < 0) { cl_perror("failed PollFunc() call in EOFcheck"); return; } /* Got input? */ if (pf[0].revents & POLLIN) { cl_log(LOG_INFO, "EOF socket %d (still) has input ready (PollFunc())" , fd); } if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) { cl_log(LOG_INFO, "EOFcheck PollFunc() bits: 0x%lx" , (unsigned long)pf[0].revents); } } static int echoserver(IPC_Channel* wchan, int repcount) { char *str; int j; int errcount = 0; IPC_Message wmsg; IPC_Message* rmsg = NULL; if (!(str = malloc(data_size))) { cl_log(LOG_ERR, "Out of memory"); exit(1); } memset(&wmsg, 0, sizeof(wmsg)); wmsg.msg_private = NULL; wmsg.msg_done = NULL; wmsg.msg_body = str; wmsg.msg_buf = NULL; wmsg.msg_ch = wchan; cl_log(LOG_INFO, "Echo server: %d reps pid %d.", repcount, getpid()); for (j=1; j <= repcount ;++j, rmsg != NULL && (rmsg->msg_done(rmsg),1)) { int rc; echomsgbody(str, data_size, j, &(wmsg.msg_len)); if ((rc = wchan->ops->send(wchan, &wmsg)) != IPC_OK) { cl_log(LOG_ERR , "echotest: send failed %d rc iter %d" , rc, j); ++errcount; continue; } /*fprintf(stderr, "+"); */ wchan->ops->waitout(wchan); checkifblocked(wchan); /*fprintf(stderr, "S"); */ /* Try and induce a failure... */ if (j == repcount) { sleep(1); } while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR); if (rc != IPC_OK) { cl_log(LOG_ERR , "echotest server: waitin failed %d rc iter %d" " errno=%d" , rc, j, errno); cl_perror("waitin"); exit(1); } /*fprintf(stderr, "-"); */ if ((rc = wchan->ops->recv(wchan, &rmsg)) != IPC_OK) { cl_log(LOG_ERR , "echotest server: recv failed %d rc iter %d" " errno=%d" , rc, j, errno); cl_perror("recv"); ++errcount; rmsg=NULL; continue; } /*fprintf(stderr, "s"); */ if (rmsg->msg_len != wmsg.msg_len) { cl_log(LOG_ERR , "echotest: length mismatch [%lu,%lu] iter %d" , (unsigned long)rmsg->msg_len , (unsigned long)wmsg.msg_len, j); ++errcount; continue; } if (strncmp(rmsg->msg_body, wmsg.msg_body, wmsg.msg_len) != 0) { cl_log(LOG_ERR , "echotest: data mismatch. iteration %d" , j); ++errcount; continue; } } cl_log(LOG_INFO, "echoserver: %d errors", errcount); #if 0 cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)wchan); #endif wchan->ops->destroy(wchan); wchan = NULL; free(str); return errcount; } static int echoclient(IPC_Channel* rchan, int repcount) { int j; int errcount = 0; IPC_Message* rmsg; cl_log(LOG_INFO, "Echo client: %d reps pid %d." , repcount, (int)getpid()); for (j=1; j <= repcount ;++j) { int rc; while ((rc = rchan->ops->waitin(rchan)) == IPC_INTR); if (rc != IPC_OK) { cl_log(LOG_ERR , "echotest client: waitin failed %d rc iter %d" " errno=%d" , rc, j, errno); cl_perror("waitin"); exit(1); } /*fprintf(stderr, "/"); */ if ((rc = rchan->ops->recv(rchan, &rmsg)) != IPC_OK) { cl_log(LOG_ERR , "echoclient: recv failed %d rc iter %d" " errno=%d" , rc, j, errno); cl_perror("recv"); ++errcount; if (errcount > MAXERRORS_RECV) { cl_log(LOG_ERR, "echoclient: errcount excessive: %d: abandoning", errcount); exit(1); } --j; rmsg=NULL; continue; } /*fprintf(stderr, "c"); */ if ((rc = rchan->ops->send(rchan, rmsg)) != IPC_OK) { cl_log(LOG_ERR , "echoclient: send failed %d rc iter %d" , rc, j); cl_log(LOG_INFO, "Message being sent: %s" , (char*)rmsg->msg_body); ++errcount; continue; } /*fprintf(stderr, "%%"); */ rchan->ops->waitout(rchan); checkifblocked(rchan); /*fprintf(stderr, "C"); */ } cl_log(LOG_INFO, "echoclient: %d errors", errcount); #if 0 cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)rchan); #endif rchan->ops->destroy(rchan); rchan = NULL; return errcount; } void dump_ipc_info(IPC_Channel* chan); static int checkinput(IPC_Channel* chan, const char * where, int* rdcount, int maxcount) { IPC_Message* rmsg = NULL; int errs = 0; int rc; while (chan->ops->is_message_pending(chan) && errs < 10 && *rdcount < maxcount) { if (chan->ch_status == IPC_DISCONNECT && *rdcount < maxcount){ cl_log(LOG_ERR , "checkinput1[0x%lx %s]: EOF in iter %d" , (unsigned long)chan, where, *rdcount); EOFcheck(chan); } if (rmsg != NULL) { rmsg->msg_done(rmsg); rmsg = NULL; } if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) { if (chan->ch_status == IPC_DISCONNECT) { cl_log(LOG_ERR , "checkinput2[0x%lx %s]: EOF in iter %d" , (unsigned long)chan, where, *rdcount); EOFcheck(chan); return errs; } cl_log(LOG_ERR , "checkinput[%s]: recv" " failed: rc %d rdcount %d errno=%d" , where, rc, *rdcount, errno); cl_perror("recv"); rmsg=NULL; ++errs; continue; } *rdcount += 1; if (!checkmsg(rmsg, where, *rdcount)) { dump_ipc_info(chan); ++errs; } if (*rdcount < maxcount && chan->ch_status == IPC_DISCONNECT){ cl_log(LOG_ERR , "checkinput3[0x%lx %s]: EOF in iter %d" , (unsigned long)chan, where, *rdcount); EOFcheck(chan); } } return errs; } static void async_high_flow_callback(IPC_Channel* ch, void* userdata) { int* stopsending = userdata; if (userdata == NULL){ cl_log(LOG_ERR, "userdata is NULL"); return; } *stopsending = 1; } static void async_low_flow_callback(IPC_Channel* ch, void* userdata) { int* stopsending = userdata; if (userdata == NULL){ cl_log(LOG_ERR, "userdata is NULL"); return; } *stopsending = 0; } static int asyn_echoserver(IPC_Channel* wchan, int repcount) { int rdcount = 0; int wrcount = 0; int errcount = 0; int blockedcount = 0; IPC_Message* wmsg; const char* w = "asyn_echoserver"; int stopsending = 0; cl_log(LOG_INFO, "Asyn echo server: %d reps pid %d." , repcount, (int)getpid()); (void)async_high_flow_callback; (void)async_low_flow_callback; wchan->ops->set_high_flow_callback(wchan, async_high_flow_callback, &stopsending); wchan->ops->set_low_flow_callback(wchan, async_low_flow_callback, &stopsending); wchan->low_flow_mark = 2; wchan->high_flow_mark = 20; while (rdcount < repcount) { int rc; while (wrcount < repcount && blockedcount < 10 && wchan->ch_status != IPC_DISCONNECT ){ if (!stopsending){ ++wrcount; if (wrcount > repcount) { break; } wmsg = wchan->ops->new_ipcmsg(wchan, NULL, data_size, NULL); echomsgbody(wmsg->msg_body, data_size, wrcount, &wmsg->msg_len); if ((rc = wchan->ops->send(wchan, wmsg)) != IPC_OK){ cl_log(LOG_INFO, "channel sstatus in echo server is %d", wchan->ch_status); if (wchan->ch_status != IPC_CONNECT) { cl_log(LOG_ERR , "asyn_echoserver: send failed" " %d rc iter %d" , rc, wrcount); ++errcount; continue; }else {/*send failed because of channel busy * roll back */ --wrcount; } } if (wchan->ops->is_sending_blocked(wchan)) { /* fprintf(stderr, "b"); */ ++blockedcount; }else{ blockedcount = 0; } } errcount += checkinput(wchan, w, &rdcount, repcount); if (wrcount < repcount && wchan->ch_status == IPC_DISCONNECT) { ++errcount; break; } } /* cl_log(LOG_INFO, "async_echoserver: wrcount =%d rdcount=%d B", wrcount, rdcount); */ wchan->ops->waitout(wchan); errcount += checkinput(wchan, w, &rdcount, repcount); if (wrcount >= repcount && rdcount < repcount) { while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR); if (rc != IPC_OK) { cl_log(LOG_ERR , "asyn_echoserver: waitin()" " failed %d rc rdcount %d errno=%d" , rc, rdcount, errno); cl_perror("waitin"); exit(1); } } if (wchan->ch_status == IPC_DISCONNECT && rdcount < repcount) { cl_log(LOG_ERR, "asyn_echoserver: EOF in iter %d (wrcount=%d)", rdcount, wrcount); EOFcheck(wchan); ++errcount; break; } blockedcount = 0; } cl_log(LOG_INFO, "asyn_echoserver: %d errors", errcount); #if 0 cl_log(LOG_INFO, "%d destroying channel 0x%lx", getpid(), (unsigned long)wchan); #endif wchan->ops->destroy(wchan); wchan = NULL; return errcount; } static int asyn_echoclient(IPC_Channel* chan, int repcount) { int rdcount = 0; int wrcount = 0; int errcount = 0; IPC_Message* rmsg; int rfd = chan->ops->get_recv_select_fd(chan); int wfd = chan->ops->get_send_select_fd(chan); gboolean rdeqwr = (rfd == wfd); cl_log(LOG_INFO, "Async Echo client: %d reps pid %d." , repcount, (int)getpid()); ipc_set_pollfunc(PollFunc); while (rdcount < repcount && errcount < repcount) { int rc; struct pollfd pf[2]; int nfd = 1; pf[0].fd = rfd; pf[0].events = POLLIN|POLLHUP; if (chan->ops->is_sending_blocked(chan)) { if (rdeqwr) { pf[0].events |= POLLOUT; }else{ nfd = 2; pf[1].fd = wfd; pf[1].events = POLLOUT|POLLHUP; } } /* Have input? */ /* fprintf(stderr, "i"); */ while (chan->ops->is_message_pending(chan) && rdcount < repcount) { /*fprintf(stderr, "r"); */ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) { if (!IPC_ISRCONN(chan)) { cl_log(LOG_ERR , "Async echoclient: disconnect" " iter %d", rdcount+1); ++errcount; return errcount; } cl_log(LOG_ERR , "Async echoclient: recv" " failed %d rc iter %d errno=%d" , rc, rdcount+1, errno); cl_perror("recv"); rmsg=NULL; ++errcount; cl_log(LOG_INFO, "sleep(1)"); sleep(1); continue; } /*fprintf(stderr, "c"); */ ++rdcount; do { rc = chan->ops->send(chan, rmsg); }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT); if (chan->ch_status != IPC_CONNECT){ ++errcount; cl_perror("send"); cl_log(LOG_ERR , "Async echoclient: send failed" " rc %d, iter %d", rc, rdcount); cl_log(LOG_INFO, "Message being sent: %s" , (char*)rmsg->msg_body); if (!IPC_ISRCONN(chan)) { cl_log(LOG_ERR , "Async echoclient: EOF(2)" " iter %d", rdcount+1); EOFcheck(chan); return errcount; } continue; } ++wrcount; /*fprintf(stderr, "x"); */ } if (rdcount >= repcount) { break; } /* * At this point it is possible that the POLLOUT bit * being on is no longer necessary, but this will only * cause an extra (false) output poll iteration at worst... * This is because (IIRC) both is_sending_blocked(), and * is_message_pending() both perform a resume_io(). * This might be confusing, but -- oh well... */ /* fprintf(stderr, "P"); cl_log(LOG_INFO, "poll[%d, 0x%x]" , pf[0].fd, pf[0].events); cl_log(LOG_DEBUG, "poll[%d, 0x%x]..." , pf[0].fd, pf[0].events); fprintf(stderr, "%%"); cl_log(LOG_DEBUG, "CallingPollFunc()"); */ rc = PollFunc(pf, nfd, -1); /* Bad poll? */ if (rc <= 0) { cl_perror("Async echoclient: bad poll rc." " %d rc iter %d", rc, rdcount); ++errcount; continue; } /* Error indication? */ if ((pf[0].revents & (POLLERR|POLLNVAL)) != 0) { cl_log(LOG_ERR , "Async echoclient: bad poll revents." " revents: 0x%x iter %d", pf[0].revents, rdcount); ++errcount; continue; } /* HUP without input... Premature EOF... */ if ((pf[0].revents & POLLHUP) && ((pf[0].revents&POLLIN) == 0)) { cl_log(LOG_ERR , "Async echoclient: premature pollhup." " revents: 0x%x iter %d", pf[0].revents, rdcount); EOFcheck(chan); ++errcount; continue; } /* Error indication? */ if (nfd > 1 && (pf[1].revents & (POLLERR|POLLNVAL)) != 0) { cl_log(LOG_ERR , "Async echoclient: bad poll revents[1]." " revents: 0x%x iter %d", pf[1].revents, rdcount); ++errcount; continue; } /* Output unblocked (only) ? */ if (pf[nfd-1].revents & POLLOUT) { /*fprintf(stderr, "R");*/ chan->ops->resume_io(chan); }else if ((pf[0].revents & POLLIN) == 0) { /* Neither I nor O available... */ cl_log(LOG_ERR , "Async echoclient: bad events." " revents: 0x%x iter %d", pf[0].revents, rdcount); ++errcount; } } cl_poll_ignore(rfd); cl_poll_ignore(wfd); cl_log(LOG_INFO, "Async echoclient: %d errors, %d reads, %d writes", errcount, rdcount, wrcount); #if 0 cl_log(LOG_INFO, "%d destroying channel 0x%lx",getpid(), (unsigned long)chan); #endif chan->ops->waitout(chan); chan->ops->destroy(chan); chan = NULL; return errcount; } struct iterinfo { int wcount; int rcount; int errcount; IPC_Channel* chan; int max; gboolean sendingsuspended; }; static GMainLoop* loop = NULL; static gboolean s_send_msg(gpointer data) { struct iterinfo*i = data; IPC_Message* wmsg; int rc; ++i->wcount; wmsg = i->chan->ops->new_ipcmsg(i->chan, NULL, data_size, NULL); echomsgbody(wmsg->msg_body, data_size, i->wcount, &wmsg->msg_len); /*cl_log(LOG_INFO, "s_send_msg: sending out %d", i->wcount);*/ if ((rc = i->chan->ops->send(i->chan, wmsg)) != IPC_OK) { cl_log(LOG_ERR , "s_send_msg: send failed" " %d rc iter %d" , rc, i->wcount); cl_log(LOG_ERR , "s_send_msg: channel status: %d qlen: %ld" , i->chan->ch_status , (long)i->chan->send_queue->current_qlen); ++i->errcount; if (i->chan->ch_status != IPC_CONNECT) { cl_log(LOG_ERR, "s_send_msg: Exiting."); return FALSE; } if (i->errcount >= MAXERRORS) { g_main_quit(loop); return FALSE; } } return !i->sendingsuspended?i->wcount < i->max: FALSE; } static void mainloop_low_flow_callback(IPC_Channel* ch, void* userdata) { struct iterinfo* i = (struct iterinfo*) userdata; if (userdata == NULL){ cl_log(LOG_ERR, "userdata is NULL"); return; } if (i->sendingsuspended){ i->sendingsuspended = FALSE; g_idle_add(s_send_msg, i); } return; } static void mainloop_high_flow_callback(IPC_Channel* ch, void* userdata) { struct iterinfo* i = (struct iterinfo*) userdata; if (userdata == NULL){ cl_log(LOG_ERR, "userdata is NULL"); return; } i->sendingsuspended = TRUE; } static gboolean s_rcv_msg(IPC_Channel* chan, gpointer data) { struct iterinfo*i = data; i->errcount += checkinput(chan, "s_rcv_msg", &i->rcount, i->max); if (chan->ch_status == IPC_DISCONNECT || i->rcount >= i->max || i->errcount > MAXERRORS) { if (i->rcount < i->max) { ++i->errcount; cl_log(LOG_INFO, "Early exit from s_rcv_msg"); } g_main_quit(loop); return FALSE; } return TRUE; } static gboolean checkmsg(IPC_Message* rmsg, const char * who, int rcount) { char *str; size_t len; if (!(str = malloc(data_size))) { cl_log(LOG_ERR, "Out of memory"); exit(1); } echomsgbody(str, data_size, rcount, &len); if (rmsg->msg_len != len) { cl_log(LOG_ERR , "checkmsg[%s]: length mismatch" " [expected %u, got %lu] iteration %d" , who, (unsigned)len , (unsigned long)rmsg->msg_len , rcount); cl_log(LOG_ERR , "checkmsg[%s]: expecting [%s]" , who, str); cl_log(LOG_ERR , "checkmsg[%s]: got [%s] instead" , who, (const char *)rmsg->msg_body); return FALSE; } if (strncmp(rmsg->msg_body, str, len) != 0) { cl_log(LOG_ERR , "checkmsg[%s]: data mismatch" ". input iteration %d" , who, rcount); cl_log(LOG_ERR , "checkmsg[%s]: expecting [%s]" , who, str); cl_log(LOG_ERR , "checkmsg[%s]: got [%s] instead" , who, (const char *)rmsg->msg_body); return FALSE; #if 0 }else if (strcmp(who, "s_rcv_msg") == 0) { #if 0 || strcmp(who, "s_echo_msg") == 0) { #endif cl_log(LOG_ERR , "checkmsg[%s]: data Good" "! input iteration %d" , who, rcount); #endif } free(str); return TRUE; } static gboolean s_echo_msg(IPC_Channel* chan, gpointer data) { struct iterinfo* i = data; int rc; IPC_Message* rmsg; while (chan->ops->is_message_pending(chan)) { if (chan->ch_status == IPC_DISCONNECT) { break; } if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) { cl_log(LOG_ERR , "s_echo_msg: recv failed %d rc iter %d" " errno=%d" , rc, i->rcount+1, errno); cl_perror("recv"); ++i->errcount; goto retout; } i->rcount++; if (!checkmsg(rmsg, "s_echo_msg", i->rcount)) { ++i->errcount; } /*cl_log(LOG_INFO, "s_echo_msg: rcount= %d, wcount =%d", i->rcount, i->wcount);*/ do { rc = chan->ops->send(chan, rmsg); }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT); if (chan->ch_status != IPC_CONNECT){ cl_log(LOG_ERR, "s_echo_msg: send failed %d rc iter %d qlen %ld", rc, i->rcount, (long)chan->send_queue->current_qlen); cl_perror("send"); i->errcount ++; } i->wcount+=1; /*cl_log(LOG_INFO, "s_echo_msg: end of this ite");*/ } retout: /*fprintf(stderr, "%%");*/ if (i->rcount >= i->max || chan->ch_status == IPC_DISCONNECT || i->errcount > MAXERRORS) { chan->ops->waitout(chan); g_main_quit(loop); return FALSE; } return TRUE; } static void init_iterinfo(struct iterinfo * i, IPC_Channel* chan, int max) { memset(i, 0, sizeof(*i)); i->chan = chan; i->max = max; i->sendingsuspended = FALSE; } static int mainloop_server(IPC_Channel* chan, int repcount) { struct iterinfo info; guint sendmsgsrc; loop = g_main_new(FALSE); init_iterinfo(&info, chan, repcount); chan->ops->set_high_flow_callback(chan, mainloop_high_flow_callback, &info); chan->ops->set_low_flow_callback(chan, mainloop_low_flow_callback, &info); chan->high_flow_mark = 20; chan->low_flow_mark = 2; sendmsgsrc = g_idle_add(s_send_msg, &info); G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan , FALSE, s_rcv_msg, &info, NULL); cl_log(LOG_INFO, "Mainloop echo server: %d reps pid %d.", repcount, (int)getpid()); g_main_run(loop); g_main_destroy(loop); g_source_remove(sendmsgsrc); loop = NULL; cl_log(LOG_INFO, "Mainloop echo server: %d errors", info.errcount); return info.errcount; } static int mainloop_client(IPC_Channel* chan, int repcount) { struct iterinfo info; loop = g_main_new(FALSE); init_iterinfo(&info, chan, repcount); G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan , FALSE, s_echo_msg, &info, NULL); cl_log(LOG_INFO, "Mainloop echo client: %d reps pid %d.", repcount, (int)getpid()); g_main_run(loop); g_main_destroy(loop); loop = NULL; cl_log(LOG_INFO, "Mainloop echo client: %d errors, %d read %d written" , info.errcount, info.rcount, info.wcount); return info.errcount; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipctransient.h0000644000000000000000000000311412120057602027036 0ustar00usergroup00000000000000/* * Copyright (C) 2007 Andrew Beekhof * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef _GNU_SOURCE /* in case it was defined on the command line */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXERRORS 1000 #define MAX_IPC_FAIL 10 #define FIFO_LEN 1024 extern const char *procname; extern const char *commdir; void trans_getargs(int argc, char **argv); void default_ipctest_input_destroy(gpointer user_data); IPC_Message * create_simple_message(const char *text, IPC_Channel *ch); Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipctransientclient.c0000644000000000000000000001502512120057602030234 0ustar00usergroup00000000000000/* * Copyright (C) 2004 Andrew Beekhof * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #define MAX_MESSAGES 3 static char *messages[MAX_MESSAGES]; IPC_Message *create_simple_message(const char *text, IPC_Channel *ch); IPC_Channel *init_client_ipctest_comms( const char *child, gboolean (*dispatch)( IPC_Channel* source_data, gpointer user_data), void *user_data); gboolean transient_client_callback(IPC_Channel* server, void* private_data); void client_send_message( const char *message_text, IPC_Channel *server_channel, int iteration); #define MAXTSTMSG 1000 int main(int argc, char ** argv) { int lpc =0, iteration=0; GMainLoop* client_main = NULL; IPC_Channel *server_channel = NULL; trans_getargs(argc, argv); cl_log_set_entity(procname); cl_log_enable_stderr(TRUE); /* give the server a chance to start */ cl_log(LOG_INFO, "#--#--#--#--# Beginning test run %d against server %d...", lpc, iteration); client_main = g_main_new(FALSE); /* connect, send messages */ server_channel = init_client_ipctest_comms("echo", transient_client_callback, client_main); if(server_channel == NULL) { cl_log(LOG_ERR, "[Client %d] Could not connect to server", lpc); return 1; } for(lpc = 0; lpc < MAX_MESSAGES; lpc++) { messages[lpc] = (char *)malloc(sizeof(char)*MAXTSTMSG); } snprintf(messages[0], MAXTSTMSG , "%s_%ld%c", "hello", (long)getpid(), '\0'); snprintf(messages[1], MAXTSTMSG , "%s_%ld%c", "hello_world", (long)getpid(), '\0'); snprintf(messages[2], MAXTSTMSG , "%s_%ld%c", "hello_world_again", (long)getpid(), '\0'); for(lpc = 0; lpc < MAX_MESSAGES; lpc++) { client_send_message(messages[lpc], server_channel, lpc); } server_channel->ops->waitout(server_channel); /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ cl_log(LOG_DEBUG, "Waiting for replies from the echo server"); g_main_run(client_main); cl_log(LOG_INFO, "[Iteration %d] Client %d completed successfully", iteration, lpc); return 0; } IPC_Channel * init_client_ipctest_comms(const char *child, gboolean (*dispatch)(IPC_Channel* source_data ,gpointer user_data), void *user_data) { IPC_Channel *ch; GHashTable * attrs; int local_sock_len = 2; /* 2 = '/' + '\0' */ char *commpath = NULL; static char path[] = IPC_PATH_ATTR; local_sock_len += strlen(child); local_sock_len += strlen(commdir); commpath = (char*)malloc(sizeof(char)*local_sock_len); if (commpath == NULL){ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__); return NULL; } sprintf(commpath, "%s/%s", commdir, child); commpath[local_sock_len - 1] = '\0'; cl_log(LOG_DEBUG, "[Client] Attempting to talk on: %s", commpath); attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, commpath); ch = ipc_channel_constructor(IPC_ANYTYPE, attrs); g_hash_table_destroy(attrs); if (ch == NULL) { cl_log(LOG_ERR, "[Client] Could not access channel on: %s", commpath); return NULL; } else if(ch->ops->initiate_connection(ch) != IPC_OK) { cl_log(LOG_ERR, "[Client] Could not init comms on: %s", commpath); return NULL; } G_main_add_IPC_Channel(G_PRIORITY_LOW, ch, FALSE, dispatch, user_data, default_ipctest_input_destroy); return ch; } gboolean transient_client_callback(IPC_Channel* server, void* private_data) { int lpc = 0; IPC_Message *msg = NULL; char *buffer = NULL; static int received_responses = 0; GMainLoop *mainloop = (GMainLoop*)private_data; while(server->ops->is_message_pending(server) == TRUE) { if (server->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is the * new status of IPC_DISCONNECT */ break; } if(server->ops->recv(server, &msg) != IPC_OK) { cl_log(LOG_ERR, "[Client] Error while invoking recv()"); perror("[Client] Receive failure:"); return FALSE; } if (msg != NULL) { buffer = (char*)msg->msg_body; cl_log(LOG_DEBUG, "[Client] Got text [text=%s]", buffer); received_responses++; if(lpc < MAX_MESSAGES && strcmp(messages[lpc], buffer) != 0) { cl_log(LOG_ERR, "[Client] Received someone else's message [%s] instead of [%s]", buffer, messages[lpc]); } else if(lpc >= MAX_MESSAGES) { cl_log(LOG_ERR, "[Client] Receivedan extra message [%s]", buffer); } lpc++; msg->msg_done(msg); } else { cl_log(LOG_ERR, "[Client] No message this time"); } } if(server->ch_status == IPC_DISCONNECT) { cl_log(LOG_ERR, "[Client] Client received HUP"); return FALSE; } cl_log(LOG_DEBUG, "[Client] Processed %d IPC messages this time, %d total", lpc, received_responses); if(received_responses > 2) { cl_log(LOG_INFO, "[Client] Processed %d IPC messages, all done.", received_responses); received_responses = 0; g_main_quit(mainloop); cl_log(LOG_INFO, "[Client] Exiting."); return FALSE; } return TRUE; } void client_send_message(const char *message_text, IPC_Channel *server_channel, int iteration) { IPC_Message *a_message = NULL; if(server_channel->ch_status != IPC_CONNECT) { cl_log(LOG_WARNING, "[Client %d] Channel is in state %d before sending message [%s]", iteration, server_channel->ch_status, message_text); return; } a_message = create_simple_message(message_text, server_channel); if(a_message == NULL) { cl_log(LOG_ERR, "Could not create message to send"); } else { cl_log(LOG_DEBUG, "[Client %d] Sending message: %s", iteration, (char*)a_message->msg_body); while(server_channel->ops->send(server_channel, a_message) == IPC_FAIL) { cl_log(LOG_ERR, "[Client %d] IPC channel is blocked", iteration); cl_shortsleep(); } if(server_channel->ch_status != IPC_CONNECT) { cl_log(LOG_WARNING, "[Client %d] Channel is in state %d after sending message [%s]", iteration, server_channel->ch_status, message_text); } } } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipctransientlib.c0000644000000000000000000000517612120057602027532 0ustar00usergroup00000000000000/* * Copyright (C) 2004 Andrew Beekhof * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* for basename() on some OSes (e.g. Solaris) */ #include #define WORKING_DIR HA_VARLIBHBDIR const char *procname = NULL; const char *commdir = WORKING_DIR; void trans_getargs(int argc, char **argv) { int argflag, argerrs; procname = basename(argv[0]); argerrs = 0; while ((argflag = getopt(argc, argv, "C:")) != EOF) { switch (argflag) { case 'C': /* directory to commpath */ commdir = optarg; break; default: argerrs++; break; } } if (argerrs) { fprintf(stderr, "Usage: %s [-C commdir]\n" "\t-C : directory to commpath (default %s)\n", procname, WORKING_DIR); exit(1); } } void default_ipctest_input_destroy(gpointer user_data) { cl_log(LOG_INFO, "default_ipctest_input_destroy:received HUP"); } IPC_Message * create_simple_message(const char *text, IPC_Channel *ch) { IPC_Message *ack_msg = NULL; char *copy_text = NULL; if(text == NULL) { cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no text"); return NULL; } else if(ch == NULL) { cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no channel"); return NULL; } ack_msg = (IPC_Message *)malloc(sizeof(IPC_Message)); if (ack_msg == NULL){ cl_log(LOG_ERR, "create_simple_message:" "allocating memory for IPC_Message failed"); return NULL; } memset(ack_msg, 0, sizeof(IPC_Message)); copy_text = strdup(text); ack_msg->msg_private = NULL; ack_msg->msg_done = NULL; ack_msg->msg_body = copy_text; ack_msg->msg_ch = ch; ack_msg->msg_len = strlen(text)+1; return ack_msg; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ipctransientserver.c0000644000000000000000000001407612120057602030271 0ustar00usergroup00000000000000/* * Copyright (C) 2004 Andrew Beekhof * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include gboolean transient_server_callback(IPC_Channel *client, gpointer user_data); gboolean transient_server_connect(IPC_Channel *client_channel, gpointer user_data); int init_server_ipc_comms(const char *child, gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data), void (*channel_input_destroy)(gpointer user_data), gboolean usenormalpoll); int main(int argc, char ** argv) { int iteration = 0; GMainLoop* mainloop = NULL; trans_getargs(argc, argv); cl_log_set_entity(procname); cl_log_enable_stderr(TRUE); init_server_ipc_comms("echo", transient_server_connect, default_ipctest_input_destroy, FALSE); /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); cl_log(LOG_INFO, "#--#--#--# Echo Server %d is active...", iteration); g_main_run(mainloop); cl_log(LOG_INFO, "#--#--#--# Echo Server %d is stopped...", iteration); return 0; } int init_server_ipc_comms(const char *child, gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data), void (*channel_input_destroy)(gpointer user_data), gboolean usenormalpoll) { /* the clients wait channel is the other source of events. * This source delivers the clients connection events. * listen to this source at a relatively lower priority. */ mode_t mask; IPC_WaitConnection *wait_ch; GHashTable * attrs; int local_sock_len = 2; /* 2 = '/' + '\0' */ char *commpath = NULL; static char path[] = IPC_PATH_ATTR; local_sock_len += strlen(child); local_sock_len += strlen(commdir); commpath = (char*)malloc(sizeof(char)*local_sock_len); if (commpath == NULL){ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__); exit(1); } snprintf(commpath, local_sock_len, "%s/%s", commdir, child); commpath[local_sock_len - 1] = '\0'; attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, commpath); mask = umask(0); wait_ch = ipc_wait_conn_constructor(IPC_ANYTYPE, attrs); if (wait_ch == NULL){ cl_perror("[Server] Can't create wait channel of type %s", IPC_ANYTYPE); exit(1); } mask = umask(mask); g_hash_table_destroy(attrs); G_main_add_IPC_WaitConnection(G_PRIORITY_LOW, wait_ch, NULL, FALSE, channel_client_connect, wait_ch, /* user data passed to ?? */ channel_input_destroy); cl_log(LOG_INFO, "[Server] Listening on: %s", commpath); /* if (!usenormalpoll) { */ /* g_main_set_poll_func(cl_glibpoll); */ /* ipc_set_pollfunc(cl_poll); */ /* } */ return 0; } gboolean transient_server_callback(IPC_Channel *client, gpointer user_data) { int lpc = 0; IPC_Message *msg = NULL; char *buffer = NULL; IPC_Message *reply = NULL; int llpc = 0; cl_log(LOG_DEBUG, "channel: %p", client); cl_log(LOG_DEBUG, "Client status %d (disconnect=%d)", client->ch_status, IPC_DISCONNECT); while(client->ops->is_message_pending(client)) { if (client->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is that * the IPC status is now IPC_DISCONNECT */ break; } if(client->ops->recv(client, &msg) != IPC_OK) { cl_perror("[Server] Receive failure"); return FALSE; } if (msg != NULL) { lpc++; buffer = (char*)g_malloc(msg->msg_len+1); memcpy(buffer,msg->msg_body, msg->msg_len); buffer[msg->msg_len] = '\0'; cl_log(LOG_DEBUG, "[Server] Got xml [text=%s]", buffer); reply = create_simple_message(strdup(buffer), client); if (!reply) { cl_log(LOG_ERR, "[Server] Could allocate reply msg."); return FALSE; } llpc = 0; while(llpc++ < MAX_IPC_FAIL && client->ops->send(client, reply) == IPC_FAIL) { cl_log(LOG_WARNING, "[Server] ipc channel blocked"); cl_shortsleep(); } if(lpc == MAX_IPC_FAIL) { cl_log(LOG_ERR, "[Server] Could not send IPC, message. Channel is dead."); free(reply); return FALSE; } cl_log(LOG_DEBUG, "[Server] Sent reply"); msg->msg_done(msg); } else { cl_log(LOG_ERR, "[Server] No message this time"); continue; } } cl_log(LOG_DEBUG, "[Server] Processed %d messages", lpc); cl_log(LOG_DEBUG, "[Server] Client status %d", client->ch_status); if(client->ch_status != IPC_CONNECT) { cl_log(LOG_INFO, "[Server] Server received HUP from child"); return FALSE; } return TRUE; } gboolean transient_server_connect(IPC_Channel *client_channel, gpointer user_data) { /* assign the client to be something, or put in a hashtable */ cl_log(LOG_DEBUG, "A client tried to connect... and there was much rejoicing."); if(client_channel == NULL) { cl_log(LOG_ERR, "[Server] Channel was NULL"); } else if(client_channel->ch_status == IPC_DISCONNECT) { cl_log(LOG_ERR, "[Server] Channel was disconnected"); } else { cl_log(LOG_DEBUG, "[Server] Client is %s %p", client_channel == NULL?"NULL":"valid", client_channel); cl_log(LOG_DEBUG, "[Server] Client status %d (disconnect=%d)", client_channel->ch_status, IPC_DISCONNECT); cl_log(LOG_DEBUG, "[Server] Adding IPC Channel to main thread."); G_main_add_IPC_Channel(G_PRIORITY_LOW, client_channel, FALSE, transient_server_callback, NULL, default_ipctest_input_destroy); } return TRUE; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/longclock.c0000644000000000000000000001460312120057602026306 0ustar00usergroup00000000000000/* * Longclock operations * * Copyright (c) 2002 International Business Machines * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include static unsigned Hz = 0; static longclock_t Lc_Hz; static double d_Hz; const longclock_t zero_longclock = 0UL; #ifndef CLOCK_T_IS_LONG_ENOUGH # undef time_longclock #endif #ifdef HAVE_LONGCLOCK_ARITHMETIC # undef msto_longclock # undef longclockto_ms # undef secsto_longclock # undef add_longclock # undef sub_longclock # undef cmp_longclock #endif unsigned hz_longclock(void) { if (Hz == 0) { /* Compute various hz-related constants */ Hz = sysconf(_SC_CLK_TCK); Lc_Hz = (longclock_t)Hz; d_Hz = (double) Hz; } return Hz; } #ifdef TIMES_ALLOWS_NULL_PARAM # define TIMES_PARAM NULL #else static struct tms dummy_longclock_tms_struct; # define TIMES_PARAM &dummy_longclock_tms_struct #endif unsigned long cl_times(void) /* Make times(2) behave rationally on Linux */ { clock_t ret; #ifndef DISABLE_TIMES_KLUDGE int save_errno = errno; /* * times(2) really returns an unsigned value ... * * We don't check to see if we got back the error value (-1), because * the only possibility for an error would be if the address of * longclock_dummy_tms_struct was invalid. Since it's a * compiler-generated address, we assume that errors are impossible. * And, unfortunately, it is quite possible for the correct return * from times(2) to be exactly (clock_t)-1. Sigh... * */ errno = 0; #endif /* DISABLE_TIMES_KLUDGE */ ret = times(TIMES_PARAM); #ifndef DISABLE_TIMES_KLUDGE /* * This is to work around a bug in the system call interface * for times(2) found in glibc on Linux (and maybe elsewhere) * It changes the return values from -1 to -4096 all into * -1 and then dumps the -(return value) into errno. * * This totally bizarre behavior seems to be widespread in * versions of Linux and glibc. * * Many thanks to Wolfgang Dumhs * for finding and documenting this bizarre behavior. */ if (errno != 0) { ret = (clock_t) (-errno); } errno = save_errno; #endif /* DISABLE_TIMES_KLUDGE */ /* sizeof(long) may be larger than sizeof(clock_t). * Don't jump from 0x7fffffff to 0xffffffff80000000 * because of sign extension. * We do expect sizeof(clock_t) <= sizeof(long), however. */ BUILD_BUG_ON(sizeof(clock_t) > sizeof(unsigned long)); #define CLOCK_T_MAX (~0UL >> (8*(sizeof(unsigned long) - sizeof(clock_t)))) return (unsigned long)ret & CLOCK_T_MAX; } #ifdef CLOCK_T_IS_LONG_ENOUGH longclock_t time_longclock(void) { /* See note below about deliberately ignoring errors... */ return (longclock_t)cl_times(); } #else /* clock_t is shorter than 64 bits */ #define BITSPERBYTE 8 #define WRAPSHIFT (BITSPERBYTE*sizeof(clock_t)) #define WRAPAMOUNT (((longclock_t) 1) << WRAPSHIFT) #define MINJUMP ((CLOCK_T_MAX/100UL)*99UL) longclock_t time_longclock(void) { /* Internal note: This updates the static fields; care should be * taken to not call a function like cl_log (which internally * calls time_longclock() as well) just before this happens, * because then this can recurse infinitely; that is why the * cl_log call is where it is; found by Simon Graham. */ static gboolean calledbefore = FALSE; static unsigned long lasttimes = 0L; static unsigned long callcount = 0L; static longclock_t lc_wrapcount = 0L; unsigned long timesval; ++callcount; timesval = cl_times(); if (calledbefore && timesval < lasttimes) { unsigned long jumpbackby = lasttimes - timesval; if (jumpbackby < MINJUMP) { /* Kernel weirdness */ cl_log(LOG_CRIT , "%s: clock_t from times(2) appears to" " have jumped backwards (in error)!" , __FUNCTION__); cl_log(LOG_CRIT , "%s: old value was %lu" ", new value is %lu, diff is %lu, callcount %lu" , __FUNCTION__ , (unsigned long)lasttimes , (unsigned long)timesval , (unsigned long)jumpbackby , callcount); /* Assume jump back was the error and ignore it */ /* (i.e., hope it goes away) */ }else{ /* Normal looking wraparound */ /* update last time BEFORE loging as log call can call this routine recursively leading to double update of wrapcount! */ lasttimes = timesval; lc_wrapcount += WRAPAMOUNT; cl_log(LOG_INFO , "%s: clock_t wrapped around (uptime)." , __FUNCTION__); } } else { lasttimes = timesval; calledbefore = TRUE; } return (lc_wrapcount | timesval); } #endif /* ! CLOCK_T_IS_LONG_ENOUGH */ longclock_t msto_longclock(unsigned long ms) { unsigned long secs = ms / 1000UL; unsigned long msec = ms % 1000; longclock_t result; (void)(Hz == 0 && hz_longclock()); if (ms == 0) { return (longclock_t)0UL; } result = secs * Lc_Hz + (msec * Lc_Hz)/1000; if (result == 0) { result = 1; } return result; } longclock_t secsto_longclock(unsigned long Secs) { longclock_t secs = Secs; (void)(Hz == 0 && hz_longclock()); return secs * Lc_Hz; } longclock_t dsecsto_longclock(double v) { (void)(Hz == 0 && hz_longclock()); return (longclock_t) ((v * d_Hz)+0.5); } unsigned long longclockto_ms(longclock_t t) { (void)(Hz == 0 && hz_longclock()); if (t == 0) { return 0UL; } return (unsigned long) ((t*1000UL)/Lc_Hz); } #ifndef CLOCK_T_IS_LONG_ENOUGH long longclockto_long(longclock_t t) { return ((long)(t)); } longclock_t add_longclock(longclock_t l, longclock_t r) { return l + r; } longclock_t sub_longclock(longclock_t l, longclock_t r) { return l - r; } int cmp_longclock(longclock_t l, longclock_t r) { if (l < r) { return -1; } if (l > r) { return 1; } return 0; } #endif /* CLOCK_T_IS_LONG_ENOUGH */ Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/md5.c0000644000000000000000000002420612120057602025020 0ustar00usergroup00000000000000/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Cleaned up for heartbeat by * Mitja Sarp * Sun Jiang Dong * Pan Jia Ming * */ #include #ifdef HAVE_STDINT_H #include #endif #include /* for sprintf() */ #include /* for memcpy() */ #include /* for stupid systems */ #include /* for ntohl() */ #include #include #define MD5_DIGESTSIZE 16 #define MD5_BLOCKSIZE 64 typedef struct MD5Context_st { uint32_t buf[4]; uint32_t bytes[2]; uint32_t in[16]; }MD5Context; #define md5byte unsigned char struct MD5Context { uint32_t buf[4]; uint32_t bytes[2]; uint32_t in[16]; }; void MD5Init(MD5Context *context); void MD5Update(MD5Context *context, md5byte const *buf, unsigned len); void MD5Final(unsigned char digest[16], MD5Context *context); void MD5Transform(uint32_t buf[4], uint32_t const in[16]); #ifdef CONFIG_BIG_ENDIAN static inline void byteSwap(uint32_t * buf, uint32_t len); static inline void byteSwap(uint32_t * buf, uint32_t len) { int i; for (i = 0; i < len; i ++) { uint32_t tmp = buf[i]; buf[i] = ( (uint32_t) ((unsigned char *) &tmp)[0] ) | (((uint32_t) ((unsigned char *) &tmp)[1]) << 8) | (((uint32_t) ((unsigned char *) &tmp)[2]) << 16) | (((uint32_t) ((unsigned char *) &tmp)[3]) << 24); } } #elif defined(CONFIG_LITTLE_ENDIAN) #define byteSwap(buf,words) #else #error "Neither CONFIG_BIG_ENDIAN nor CONFIG_LITTLE_ENDIAN defined!" #endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(MD5Context *ctx) { ctx->buf[0] = 0x67452301ul; ctx->buf[1] = 0xefcdab89ul; ctx->buf[2] = 0x98badcfeul; ctx->buf[3] = 0x10325476ul; ctx->bytes[0] = 0; ctx->bytes[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len) { uint32_t t; /* Update byte count */ t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) ctx->bytes[1]++; /* Carry from low to high */ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if (t > len) { memcpy((md5byte *)ctx->in + 64 - t, buf, len); return; } /* First chunk is an odd size */ memcpy((md5byte *)ctx->in + 64 - t, buf, t); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += t; len -= t; /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(md5byte digest[16], MD5Context *ctx) { int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ md5byte *p = (md5byte *)ctx->in + count; /* Set the first char of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count + 8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); p = (md5byte *)ctx->in; count = 56; } memset(p, 0, count); byteSwap(ctx->in, 14); /* Append length in bits and transform */ ctx->in[14] = ctx->bytes[0] << 3; ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->buf, ctx->in); byteSwap(ctx->buf, 16); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) ((x) ^ (y) ^ (z)) #define F4(x, y, z) ((y) ^ ((x) | ~(z))) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) \ (w += f(x,y,z) + (in), (w) = ((w)<<(s) | (w)>>(32-(s))) + (x)) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478ul, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756ul, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbul, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeul, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faful, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aul, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613ul, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501ul, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8ul, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7aful, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1ul, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beul, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122ul, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193ul, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eul, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821ul, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562ul, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340ul, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51ul, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaul, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dul, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453ul, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681ul, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8ul, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6ul, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6ul, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87ul, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edul, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905ul, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8ul, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9ul, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aul, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942ul, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681ul, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122ul, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cul, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44ul, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9ul, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60ul, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70ul, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6ul, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faul, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085ul, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05ul, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039ul, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5ul, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8ul, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665ul, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244ul, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97ul, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7ul, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039ul, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3ul, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92ul, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dul, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1ul, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4ful, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0ul, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314ul, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1ul, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82ul, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235ul, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbul, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391ul, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } int MD5( const unsigned char *data , unsigned long len , unsigned char *digest) { MD5Context context; MD5Init(&context); MD5Update(&context, data, len); MD5Final(digest, &context); return 0; } int HMAC( const unsigned char * key , unsigned int key_len , const unsigned char * text , unsigned long textlen , unsigned char * digest) { MD5Context context; /* inner padding - key XORd with ipad */ unsigned char k_ipad[65]; /* outer padding - * key XORd with opad */ unsigned char k_opad[65]; unsigned char tk[MD5_DIGESTSIZE]; int i; /* if key is longer than MD5_BLOCKSIZE bytes reset it to key=MD5(key) */ if (key_len > MD5_BLOCKSIZE) { MD5Context tctx; MD5Init(&tctx); MD5Update(&tctx, (const unsigned char *)key, key_len); MD5Final(tk, &tctx); key = (unsigned char *)tk; key_len = MD5_DIGESTSIZE; } /* start out by storing key in pads */ memset(k_ipad, 0, sizeof k_ipad); memset(k_opad, 0, sizeof k_opad); memcpy(k_ipad, key, key_len); memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i #include #include #include #include #include #include /* * A slightly safer version of mkstemp(3) * * In this version, the file is initially created mode 0, and then chmod-ed * to the requested permissions. This guarantees that the file is never * open to others beyond the specified permissions at any time. */ int mkstemp_mode(char* template, mode_t filemode) { mode_t maskval; int fd; maskval = umask(0777); /* created file should now be mode 0000 */ fd = mkstemp(template); umask(maskval); /* cannot fail :-) */ if (fd >= 0) { if (chmod(template, filemode) < 0) { int save = errno; close(fd); errno = save; fd = -1; } } return fd; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/netstring_test.c0000644000000000000000000001477012120057602027414 0ustar00usergroup00000000000000/* * netstring_test: Test program for testing the heartbeat binary/struct API * * Copyright (C) 2000 Guochun Shi * * This library 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * A heartbeat API test program... */ void NodeStatus(const char * node, const char * status, void * private); void LinkStatus(const char * node, const char *, const char *, void*); void gotsig(int nsig); void NodeStatus(const char * node, const char * status, void * private) { cl_log(LOG_NOTICE, "Status update: Node %s now has status %s" , node, status); } void LinkStatus(const char * node, const char * lnk, const char * status , void * private) { cl_log(LOG_NOTICE, "Link Status update: Link %s/%s now has status %s" , node, lnk, status); } int quitnow = 0; void gotsig(int nsig) { (void)nsig; quitnow = 1; } #define BUFSIZE 16 extern int netstring_format; int main(int argc, char ** argv) { struct ha_msg* reply; struct ha_msg* pingreq = NULL; unsigned fmask; ll_cluster_t* hb; int msgcount=0; char databuf[BUFSIZE]; int i; #if 0 char * ctmp; const char * cval; int j; #endif netstring_format = 0; cl_log_set_entity(argv[0]); cl_log_enable_stderr(TRUE); cl_log_set_facility(LOG_USER); hb = ll_cluster_new("heartbeat"); cl_log(LOG_INFO, "PID=%ld", (long)getpid()); cl_log(LOG_INFO, "Signing in with heartbeat"); if (hb->llc_ops->signon(hb, "ping")!= HA_OK) { cl_log(LOG_ERR, "Cannot sign on with heartbeat"); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(1); } if (hb->llc_ops->set_nstatus_callback(hb, NodeStatus, NULL) !=HA_OK){ cl_log(LOG_ERR, "Cannot set node status callback"); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(2); } if (hb->llc_ops->set_ifstatus_callback(hb, LinkStatus, NULL)!=HA_OK){ cl_log(LOG_ERR, "Cannot set if status callback"); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(3); } #if 0 fmask = LLC_FILTER_RAW; #else fmask = LLC_FILTER_DEFAULT; #endif /* This isn't necessary -- you don't need this call - it's just for testing... */ cl_log(LOG_INFO, "Setting message filter mode"); if (hb->llc_ops->setfmode(hb, fmask) != HA_OK) { cl_log(LOG_ERR, "Cannot set filter mode"); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(4); } CL_SIGINTERRUPT(SIGINT, 1); CL_SIGNAL(SIGINT, gotsig); pingreq = ha_msg_new(0); ha_msg_add(pingreq, F_TYPE, "ping"); { struct ha_msg *childmsg; struct ha_msg *grandchildmsg; for(i = 0 ;i < BUFSIZE;i ++){ databuf[i] = 1 + i ; } databuf[4] = 0; ha_msg_addbin(pingreq, "data",databuf , BUFSIZE); childmsg = ha_msg_new(0); ha_msg_add(childmsg, "name","testchild"); ha_msg_addbin(childmsg, "data",databuf , BUFSIZE); grandchildmsg = ha_msg_new(0); ha_msg_add(grandchildmsg, "name","grandchild"); ha_msg_addstruct(childmsg, "child",grandchildmsg); if( ha_msg_addstruct(pingreq, "child", childmsg) != HA_OK){ cl_log(LOG_ERR, "adding a child message to the message failed"); exit(1); } } cl_log(LOG_INFO, "printing out the pingreq message:"); ha_log_message(pingreq); if (hb->llc_ops->sendclustermsg(hb, pingreq) == HA_OK) { cl_log(LOG_INFO, "Sent ping request to cluster"); }else{ cl_log(LOG_ERR, "PING request FAIL to cluster"); } errno = 0; for(; !quitnow && (reply=hb->llc_ops->readmsg(hb, 1)) != NULL;) { const char * type; const char * orig; ++msgcount; if ((type = ha_msg_value(reply, F_TYPE)) == NULL) { type = "?"; } if ((orig = ha_msg_value(reply, F_ORIG)) == NULL) { orig = "?"; } cl_log(LOG_INFO, " "); cl_log(LOG_NOTICE, "Got message %d of type [%s] from [%s]" , msgcount, type, orig); if (strcmp(type, "ping") ==0) { int datalen = 0; const char *data; struct ha_msg *childmsg; cl_log(LOG_INFO, "****************************************"); ha_log_message(reply); data = cl_get_binary(reply, "data", &datalen); if(data){ cl_log(LOG_INFO, " "); cl_log(LOG_INFO, "%d of data received,data=%s", datalen,data); for(i = 0; i < datalen; i++){ if( databuf[i] != data[i]){ cl_log(LOG_ERR, "data does not match at %d",i); break; } } if(i == datalen){ cl_log(LOG_INFO,"data matches"); } }else { cl_log(LOG_WARNING, "cl_get_binary failed"); } childmsg = cl_get_struct(reply,"child"); if(childmsg){ cl_log(LOG_INFO, " "); cl_log(LOG_INFO, "child message found"); ha_log_message(childmsg); }else{ cl_log(LOG_WARNING, "cl_get_struct failed"); } } #if 1 { struct ha_msg *cpmsg; cl_log(LOG_INFO, " "); cl_log(LOG_INFO, "****************************************************"); cl_log(LOG_INFO, "Testing ha_msg_copy():"); cpmsg = ha_msg_copy(reply); cl_log(LOG_INFO, " "); cl_log(LOG_INFO, "orginal message is :"); cl_log(LOG_INFO, " "); ha_log_message(reply); cl_log(LOG_INFO, " "); cl_log(LOG_INFO, "copied message is: "); cl_log(LOG_INFO, " "); ha_log_message(cpmsg); ha_msg_del(cpmsg); } ha_msg_del(reply); reply=NULL; #endif } if (!quitnow) { cl_log(LOG_ERR, "read_hb_msg returned NULL"); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); } if (hb->llc_ops->signoff(hb, TRUE) != HA_OK) { cl_log(LOG_ERR, "Cannot sign off from heartbeat."); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(10); } if (hb->llc_ops->delete(hb) != HA_OK) { cl_log(LOG_ERR, "Cannot delete API object."); cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb)); exit(11); } return 0; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/ocf_ipc.c0000644000000000000000000003264212120057602025740 0ustar00usergroup00000000000000/* * * ocf_ipc.c: IPC abstraction implementation. * * * Copyright (c) 2002 Xiaoxiang Liu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include static int num_pool_allocated = 0; static int num_pool_freed = 0; #ifdef IPC_TIME_DEBUG struct ha_msg; void cl_log_message (int log_level, const struct ha_msg *m); int timediff(longclock_t t1, longclock_t t2); void ha_msg_del(struct ha_msg* msg); void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos); #endif struct IPC_WAIT_CONNECTION * socket_wait_conn_new(GHashTable* ch_attrs); struct IPC_CHANNEL * socket_client_channel_new(GHashTable* ch_attrs); int (*ipc_pollfunc_ptr)(struct pollfd*, unsigned int, int) = (int (*)(struct pollfd*, unsigned int, int)) poll; /* Set the IPC poll function to the given function */ void ipc_set_pollfunc(int (*pf)(struct pollfd*, unsigned int, int)) { ipc_pollfunc_ptr = pf; } struct IPC_WAIT_CONNECTION * ipc_wait_conn_constructor(const char * ch_type, GHashTable* ch_attrs) { if (strcmp(ch_type, "domain_socket") == 0 || strcmp(ch_type, IPC_UDS_CRED) == 0 || strcmp(ch_type, IPC_ANYTYPE) == 0 || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) { return socket_wait_conn_new(ch_attrs); } return NULL; } struct IPC_CHANNEL * ipc_channel_constructor(const char * ch_type, GHashTable* ch_attrs) { if (strcmp(ch_type, "domain_socket") == 0 || strcmp(ch_type, IPC_UDS_CRED) == 0 || strcmp(ch_type, IPC_ANYTYPE) == 0 || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) { return socket_client_channel_new(ch_attrs); } return NULL; } static int gnametonum(const char * gname, int gnlen) { char grpname[64]; struct group* grp; if (isdigit((int) gname[0])) { return atoi(gname); } if (gnlen >= (int)sizeof(grpname)) { return -1; } strncpy(grpname, gname, gnlen); grpname[gnlen] = EOS; if ((grp = getgrnam(grpname)) == NULL) { cl_log(LOG_ERR , "Invalid group name [%s]", grpname); return -1; } return (int)grp->gr_gid; } static int unametonum(const char * lname, int llen) { char loginname[64]; struct passwd* pwd; if (llen >= (int)sizeof(loginname)) { cl_log(LOG_ERR , "user id name [%s] is too long", loginname); return -1; } strncpy(loginname, lname, llen); loginname[llen] = EOS; if (isdigit((int) loginname[0])) { return atoi(loginname); } if ((pwd = getpwnam(loginname)) == NULL) { cl_log(LOG_ERR , "Invalid user id name [%s]", loginname); return -1; } return (int)pwd->pw_uid; } static GHashTable* make_id_table(const char * list, int listlen, int (*map)(const char *, int)) { GHashTable* ret; const char * id; const char * lastid = list + listlen; int idlen; int idval; static int one = 1; ret = g_hash_table_new(g_direct_hash, g_direct_equal); id = list; while (id < lastid && *id != EOS) { idlen = strcspn(id, ","); if (id+idlen >= lastid) { idlen = (lastid - id); } idval = map(id, idlen); if (idval < 0) { g_hash_table_destroy(ret); return NULL; } #if 0 cl_log(LOG_DEBUG , "Adding [ug]id %*s [%d] to authorization g_hash_table" , idlen, id, idval); #endif g_hash_table_insert(ret, GUINT_TO_POINTER(idval), &one); id += idlen; if (id < lastid) { id += strspn(id, ","); } } return ret; } struct IPC_AUTH* ipc_str_to_auth(const char* uidlist, int uidlen, const char* gidlist, int gidlen) { struct IPC_AUTH* auth; auth = malloc(sizeof(struct IPC_AUTH)); if (auth == NULL) { cl_log(LOG_ERR, "Out of memory for IPC_AUTH"); return NULL; } memset(auth, 0, sizeof(*auth)); if (uidlist) { auth->uid = make_id_table(uidlist, uidlen, unametonum); if (auth->uid == NULL) { cl_log(LOG_ERR, "Bad uid list [%*s]", uidlen, uidlist); goto errout; } } if (gidlist) { auth->gid = make_id_table(gidlist, gidlen, gnametonum); if (auth->gid == NULL) { cl_log(LOG_ERR , "Bad gid list [%*s]", gidlen, gidlist); goto errout; } } return auth; errout: if (auth->uid) { g_hash_table_destroy(auth->uid); auth->uid = NULL; } if (auth->gid) { g_hash_table_destroy(auth->gid); auth->gid = NULL; } free(auth); auth = NULL; return NULL; } struct IPC_AUTH * ipc_set_auth(uid_t * a_uid, gid_t * a_gid, int num_uid, int num_gid) { struct IPC_AUTH *temp_auth; int i; static int v = 1; temp_auth = malloc(sizeof(struct IPC_AUTH)); if (temp_auth == NULL) { cl_log(LOG_ERR, "%s: memory allocation failed",__FUNCTION__); return NULL; } temp_auth->uid = g_hash_table_new(g_direct_hash, g_direct_equal); temp_auth->gid = g_hash_table_new(g_direct_hash, g_direct_equal); if (num_uid > 0) { for (i=0; iuid, GINT_TO_POINTER((gint)a_uid[i]) , &v); } } if (num_gid > 0) { for (i=0; igid, GINT_TO_POINTER((gint)a_gid[i]) , &v); } } return temp_auth; } void ipc_destroy_auth(struct IPC_AUTH *auth) { if (auth != NULL) { if (auth->uid) { g_hash_table_destroy(auth->uid); } if (auth->gid) { g_hash_table_destroy(auth->gid); } free((void *)auth); } } static void ipc_bufpool_display(struct ipc_bufpool* pool) { if (pool == NULL) { return; } cl_log(LOG_INFO, "pool: refcount=%d, startpos=%p, currpos=%p," "consumepos=%p, endpos=%p, size=%d", pool->refcount, pool->startpos, pool->currpos, pool->consumepos, pool->endpos, pool->size); } void ipc_bufpool_dump_stats(void) { cl_log(LOG_INFO, "num_pool_allocated=%d, num_pool_freed=%d, diff=%d", num_pool_allocated, num_pool_freed, num_pool_allocated - num_pool_freed); } #define POOLHDR_SIZE \ (sizeof(struct ipc_bufpool) + 2*sizeof(struct SOCKET_MSG_HEAD)) struct ipc_bufpool* ipc_bufpool_new(int size) { struct ipc_bufpool* pool; int totalsize; /* there are memories for two struct SOCKET_MSG_HEAD * one for the big message, the other one for the next * message. This code prevents allocating * <4k> <4k> ... * from happening when a client sends big messages * constantly*/ totalsize = size + POOLHDR_SIZE; if (totalsize < POOL_SIZE) { totalsize = POOL_SIZE; } if (totalsize > MAXMSG + POOLHDR_SIZE) { cl_log(LOG_INFO, "ipc_bufpool_new: " "asking for buffer with size %d; " "corrupted data len???", totalsize); return NULL; } pool = (struct ipc_bufpool*)malloc(totalsize+1); if (pool == NULL) { cl_log(LOG_ERR, "%s: memory allocation failed", __FUNCTION__); return NULL; } memset(pool, 0, totalsize); pool->refcount = 1; pool->startpos = pool->currpos = pool->consumepos = ((char*)pool) + sizeof(struct ipc_bufpool); pool->endpos = ((char*)pool) + totalsize; pool->size = totalsize; num_pool_allocated ++ ; return pool; } void ipc_bufpool_del(struct ipc_bufpool* pool) { if (pool == NULL) { return; } if (pool->refcount > 0) { cl_log(LOG_ERR," ipc_bufpool_del:" " IPC buffer pool reference count > 0"); return; } memset(pool, 0, pool->size); free(pool); num_pool_freed ++ ; } int ipc_bufpool_spaceleft(struct ipc_bufpool* pool) { if( pool == NULL) { cl_log(LOG_ERR, "ipc_bufpool_spaceleft:" " invalid input argument"); return 0; } return pool->endpos - pool->currpos; } /* brief free the memory space allocated to msg and destroy msg. */ static void ipc_bufpool_msg_done(struct IPC_MESSAGE * msg) { struct ipc_bufpool* pool; if (msg == NULL) { cl_log(LOG_ERR, "ipc_bufpool_msg_done: invalid input"); return; } pool = (struct ipc_bufpool*)msg->msg_private; ipc_bufpool_unref(pool); free(msg); } static struct IPC_MESSAGE* ipc_bufpool_msg_new(void) { struct IPC_MESSAGE * temp_msg; temp_msg = malloc(sizeof(struct IPC_MESSAGE)); if (temp_msg == NULL) { cl_log(LOG_ERR, "ipc_bufpool_msg_new:" "allocating new msg failed"); return NULL; } memset(temp_msg, 0, sizeof(struct IPC_MESSAGE)); return temp_msg; } static void ipcmsg_display(IPC_Message* ipcmsg) { if (ipcmsg == NULL) { cl_log(LOG_ERR, "ipcmsg is NULL"); return; } cl_log(LOG_INFO, "ipcmsg: msg_len=%lu, msg_buf=%p, msg_body=%p," "msg_done=%p, msg_private=%p, msg_ch=%p", (unsigned long)ipcmsg->msg_len, ipcmsg->msg_buf, ipcmsg->msg_body, ipcmsg->msg_done, ipcmsg->msg_private, ipcmsg->msg_ch); } /* after a recv call, we have new data * in the pool buf, we need to update our * pool struct to consume it * */ int ipc_bufpool_update(struct ipc_bufpool* pool, struct IPC_CHANNEL * ch, int msg_len, IPC_Queue* rqueue) { IPC_Message* ipcmsg; struct SOCKET_MSG_HEAD localhead; struct SOCKET_MSG_HEAD* head = &localhead; int nmsgs = 0 ; if (rqueue == NULL) { cl_log(LOG_ERR, "ipc_update_bufpool:" "invalid input"); return 0; } pool->currpos += msg_len; while(TRUE) { /*not enough data for head*/ if ((int)(pool->currpos - pool->consumepos) < (int)ch->msgpad) { break; } memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD)); if (head->magic != HEADMAGIC) { GList* last = g_list_last(rqueue->queue); cl_log(LOG_ERR, "ipc_bufpool_update: " "magic number in head does not match." "Something very bad happened, abort now, farside pid =%d", ch->farside_pid); cl_log(LOG_ERR, "magic=%x, expected value=%x", head->magic, HEADMAGIC); ipc_bufpool_display(pool); cl_log(LOG_INFO, "nmsgs=%d", nmsgs); /*print out the last message in queue*/ if (last) { IPC_Message* m = (IPC_Message*)last; ipcmsg_display(m); } abort(); } if ( head->msg_len > MAXMSG) { cl_log(LOG_ERR, "ipc_update_bufpool:" "msg length is corruptted(%d)", head->msg_len); break; } if (pool->consumepos + ch->msgpad + head->msg_len > pool->currpos) { break; } ipcmsg = ipc_bufpool_msg_new(); if (ipcmsg == NULL) { cl_log(LOG_ERR, "ipc_update_bufpool:" "allocating memory for new ipcmsg failed"); break; } ipcmsg->msg_buf = pool->consumepos; ipcmsg->msg_body = pool->consumepos + ch->msgpad; ipcmsg->msg_len = head->msg_len; ipcmsg->msg_private = pool; ipcmsg->msg_done = ipc_bufpool_msg_done; #ifdef IPC_TIME_DEBUG ipc_time_debug(ch,ipcmsg, MSGPOS_RECV); #endif rqueue->queue = g_list_append(rqueue->queue, ipcmsg); rqueue->current_qlen ++; nmsgs++; pool->consumepos += ch->msgpad + head->msg_len; ipc_bufpool_ref(pool); } return nmsgs; } gboolean ipc_bufpool_full(struct ipc_bufpool* pool, struct IPC_CHANNEL* ch, int* dataspaceneeded) { struct SOCKET_MSG_HEAD localhead; struct SOCKET_MSG_HEAD* head = &localhead; *dataspaceneeded = 0; /* not enough space for head */ if ((int)(pool->endpos - pool->consumepos) < (int)ch->msgpad) { return TRUE; } /*enough space for head*/ if ((int)(pool->currpos - pool->consumepos) >= (int)ch->msgpad) { memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD)); /* not enough space for data*/ if ( pool->consumepos + ch->msgpad + head->msg_len >= pool->endpos) { *dataspaceneeded = head->msg_len; return TRUE; } } /* Either we are sure we have enough space * or we cannot tell because we have not received * head yet. But we are sure we have enough space * for head */ return FALSE; } int ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool, struct ipc_bufpool* srcpool) { struct SOCKET_MSG_HEAD localhead; struct SOCKET_MSG_HEAD *head = &localhead; int space_needed; int nbytes; if (dstpool == NULL || srcpool == NULL) { cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:" "invalid input"); return IPC_FAIL; } if (srcpool->currpos - srcpool->consumepos >= (ssize_t)sizeof(struct SOCKET_MSG_HEAD)) { memcpy(head, srcpool->consumepos, sizeof(struct SOCKET_MSG_HEAD)); space_needed = head->msg_len + sizeof(*head); if (space_needed > ipc_bufpool_spaceleft(dstpool)) { cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:" " not enough space left in dst pool,spaced needed=%d", space_needed); return IPC_FAIL; } } nbytes = srcpool->currpos - srcpool->consumepos; memcpy(dstpool->consumepos, srcpool->consumepos,nbytes); srcpool->currpos = srcpool->consumepos; dstpool->currpos = dstpool->consumepos + nbytes; return IPC_OK; } void ipc_bufpool_ref(struct ipc_bufpool* pool) { if (pool == NULL) { cl_log(LOG_ERR, "ref_pool:" " invalid input"); return; } pool->refcount ++; } void ipc_bufpool_unref(struct ipc_bufpool* pool) { if (pool == NULL) { cl_log(LOG_ERR, "unref_pool:" " invalid input"); return; } pool->refcount --; if (pool->refcount <= 0) { ipc_bufpool_del(pool); } } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/proctrack.c0000644000000000000000000002656212120057602026332 0ustar00usergroup00000000000000/* * Process tracking object. * * Copyright (c) 2002 International Business Machines * Author: Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #define DEBUGPROCTRACK debugproctrack int debugproctrack = 0; static int LoggingIsEnabled = 1; static GHashTable* ProcessTable = NULL; static void InitProcTable(void); static void ForEachProcHelper(gpointer key, gpointer value , void * helper); static gboolean TrackedProcTimeoutFunction(gpointer p); static void InitProcTable() { if (ProcessTable) { return; } ProcessTable = g_hash_table_new(g_direct_hash, g_direct_equal); } /* Create/Log a new tracked process */ void NewTrackedProc(pid_t pid, int isapgrp, ProcTrackLogType loglevel , void * privatedata, ProcTrack_ops* ops) { ProcTrack* p = g_new(ProcTrack, 1); InitProcTable(); p->pid = pid; p->isapgrp = isapgrp; p->loglevel = loglevel; p->privatedata = privatedata; p->ops = ops; p->startticks = time_longclock(); p->starttime = time(NULL); p->timerid = 0; p->timeoutseq = -1; p->killinfo = NULL; g_hash_table_insert(ProcessTable, GINT_TO_POINTER(pid), p); /* Tell them that someone registered a process */ if (p->ops->procregistered) { p->ops->procregistered(p); } } static struct signal_info_s { int signo; const char * sigdefine; const char* sigwords; } signal_info [] = { #ifdef SIGHUP {SIGHUP, "SIGHUP", "Hangup"}, #endif #ifdef SIGINT {SIGINT, "SIGINT", "Interrupt"}, #endif #ifdef SIGQUIT {SIGQUIT, "SIGQUIT", "Quit"}, #endif #ifdef SIGILL {SIGILL, "SIGILL", "Illegal instruction"}, #endif #ifdef SIGTRAP {SIGTRAP, "SIGTRAP", "Trace"}, #endif #ifdef SIGABRT {SIGABRT, "SIGABRT", "Abort"}, #endif #ifdef SIGIOT {SIGIOT, "SIGIOT", "IOT trap"}, #endif #ifdef SIGBUS {SIGBUS, "SIGBUS", "BUS error"}, #endif #ifdef SIGFPE {SIGFPE, "SIGFPE", "Floating-point exception"}, #endif #ifdef SIGKILL {SIGKILL, "SIGKILL", "Kill, unblockable"}, #endif #ifdef SIGUSR1 {SIGUSR1, "SIGUSR1", "User-defined signal 1"}, #endif #ifdef SIGSEGV {SIGSEGV, "SIGSEGV", "Segmentation violation"}, #endif #ifdef SIGUSR2 {SIGUSR2, "SIGUSR2", "User-defined signal 2"}, #endif #ifdef SIGPIPE {SIGPIPE, "SIGPIPE", "Broken pipe (POSIX)"}, #endif #ifdef SIGALRM {SIGALRM, "SIGALRM", "Alarm clock (POSIX)"}, #endif #ifdef SIGTERM {SIGTERM, "SIGTERM", "Termination (ANSI)"}, #endif #ifdef SIGSTKFLT {SIGSTKFLT, "SIGSTKFLT", "Stack fault"}, #endif #ifdef SIGCHLD {SIGCHLD, "SIGCHLD", "Child status has changed"}, #endif #ifdef SIGCLD {SIGCLD, "SIGCLD ", "Child status has changed"}, #endif #ifdef SIGCONT {SIGCONT, "SIGCONT", "Continue"}, #endif #ifdef SIGSTOP {SIGSTOP, "SIGSTOP", "Stop, unblockable"}, #endif #ifdef SIGTSTP {SIGTSTP, "SIGTSTP", "Keyboard stop"}, #endif #ifdef SIGTTIN {SIGTTIN, "SIGTTIN", "Background read from tty"}, #endif #ifdef SIGTTOU {SIGTTOU, "SIGTTOU", "Background write to tty"}, #endif #ifdef SIGURG {SIGURG, "SIGURG ", "Urgent condition on socket"}, #endif #ifdef SIGXCPU {SIGXCPU, "SIGXCPU", "CPU limit exceeded"}, #endif #ifdef SIGXFSZ {SIGXFSZ, "SIGXFSZ", "File size limit exceeded"}, #endif #ifdef SIGVTALRM {SIGVTALRM, "SIGVTALRM", "Virtual alarm clock"}, #endif #ifdef SIGPROF {SIGPROF, "SIGPROF", "Profiling alarm clock"}, #endif #ifdef SIGWINCH {SIGWINCH, "SIGWINCH", "Window size change"}, #endif #ifdef SIGPOLL {SIGPOLL, "SIGPOLL", "Pollable event occurred"}, #endif #ifdef SIGIO {SIGIO, "SIGIO", "I/O now possible"}, #endif #ifdef SIGPWR {SIGPWR, "SIGPWR", "Power failure restart"}, #endif #ifdef SIGSYS {SIGSYS, "SIGSYS", "Bad system call"}, #endif }; static const char * signal_name(int signo, const char ** sigdescription) { int j; for (j=0; j < DIMOF(signal_info); ++j) { if (signal_info[j].signo == signo) { if (sigdescription) { *sigdescription = signal_info[j].sigwords; } return signal_info[j].sigdefine; } } if (sigdescription) { *sigdescription = NULL; } return NULL; } /* returns TRUE if 'pid' was registered */ int ReportProcHasDied(int pid, int status) { ProcTrack* p; int signo=0; int deathbyexit=0; int deathbysig=0; int exitcode=0; int doreport = 0; int debugreporting = 0; const char * type; ProcTrackLogType level; #ifdef WCOREDUMP int didcoredump = 0; #endif if ((p = GetProcInfo(pid)) == NULL) { if (DEBUGPROCTRACK) { cl_log(LOG_DEBUG , "Process %d died (%d) but is not tracked." , pid, status); } type = "untracked process"; level = PT_LOGNONE; }else{ type = p->ops->proctype(p); level = p->loglevel; } if (WIFEXITED(status)) { deathbyexit=1; exitcode = WEXITSTATUS(status); }else if (WIFSIGNALED(status)) { deathbysig=1; signo = WTERMSIG(status); doreport=1; } switch(level) { case PT_LOGVERBOSE: doreport=1; break; case PT_LOGNONE: doreport = 0; break; case PT_LOGNORMAL: break; } if (!LoggingIsEnabled) { doreport = 0; } #ifdef WCOREDUMP if (WCOREDUMP(status)) { /* Force a report on all core dumping processes */ didcoredump=1; doreport=1; } #endif if (DEBUGPROCTRACK && !doreport) { doreport = 1; debugreporting = 1; } if (doreport) { if (deathbyexit) { cl_log((exitcode == 0 ? LOG_INFO : LOG_WARNING) , "Managed %s process %d exited with return code %d." , type, pid, exitcode); }else if (deathbysig) { const char * signame = NULL; const char * sigwords = NULL; int logtype; signame = signal_name(signo, &sigwords); logtype = (debugreporting ? LOG_INFO : LOG_WARNING); /* * Processes being killed isn't an error if * we're only logging because of debugging. */ if (signame && sigwords) { cl_log(logtype , "Managed %s process %d killed by" " signal %d [%s - %s]." , type, pid, signo , signame, sigwords); }else{ cl_log(logtype , "Managed %s process %d killed by signal %d." , type, pid, signo); } }else{ cl_log(LOG_ERR, "Managed %s process %d went away" " strangely (!)" , type, pid); } } #ifdef WCOREDUMP if (didcoredump) { /* We report ALL core dumps without exception */ cl_log(LOG_ERR, "Managed %s process %d dumped core" , type, pid); } #endif if (p) { RemoveTrackedProcTimeouts(pid); /* * From clplumbing/proctrack.h: * (ProcTrack* p, int status, int signo, int exitcode * , int waslogged); */ p->ops->procdied(p, status, signo, exitcode, doreport); if (p->privatedata) { /* They may have forgotten to free something... */ cl_log(LOG_ERR, "Managed %s process %d did not" " clean up private data!" , type, pid); } g_hash_table_remove(ProcessTable, GINT_TO_POINTER(pid)); g_free(p); } return doreport; } /* Return information associated with the given PID (or NULL) */ ProcTrack* GetProcInfo(pid_t pid) { return (ProcessTable ? g_hash_table_lookup(ProcessTable, GINT_TO_POINTER(pid)) : NULL); } /* "info" is 0-terminated (terminated by a 0 signal) */ int SetTrackedProcTimeouts(pid_t pid, ProcTrackKillInfo* info) { long mstimeout; ProcTrack* pinfo; pinfo = GetProcInfo(pid); if (pinfo == NULL) { return 0; } pinfo->timeoutseq = 0; pinfo->killinfo = info; mstimeout = pinfo->killinfo[0].mstimeout; pinfo->timerid = Gmain_timeout_add(mstimeout , TrackedProcTimeoutFunction , GINT_TO_POINTER(pid)); return pinfo->timerid; } void RemoveTrackedProcTimeouts(pid_t pid) { ProcTrack* pinfo; pinfo = GetProcInfo(pid); if (pinfo == NULL) { return; } if (pinfo->killinfo && pinfo->timerid) { Gmain_timeout_remove(pinfo->timerid); } pinfo->killinfo = NULL; pinfo->timerid = 0; } static gboolean TrackedProcTimeoutFunction(gpointer p) { /* This is safe - Pids are relatively small ints */ pid_t pid = POINTER_TO_SIZE_T(p); /*pointer cast as int*/ ProcTrack* pinfo; int nsig; long mstimeout; int hadprivs; pinfo = GetProcInfo(pid); if (pinfo == NULL) { cl_log(LOG_ERR, "%s: bad pinfo in call (pid %d)", __FUNCTION__, pid); return FALSE; } if (pinfo->timeoutseq < 0 || pinfo->killinfo == NULL) { cl_log(LOG_ERR , "%s: bad call (pid %d): killinfo (%d, 0x%lx)" , __FUNCTION__, pid , pinfo->timeoutseq , (unsigned long)POINTER_TO_SIZE_T(pinfo->killinfo)); return FALSE; } pinfo->timerid = 0; nsig = pinfo->killinfo[pinfo->timeoutseq].signalno; if (nsig == 0) { if (CL_PID_EXISTS(pid)) { cl_log(LOG_ERR , "%s: %s process (PID %d) will not die!" , __FUNCTION__ , pinfo->ops->proctype(pinfo) , (int)pid); } return FALSE; } pinfo->timeoutseq++; cl_log(LOG_WARNING, "%s process (PID %d) timed out (try %d)" ". Killing with signal %s (%d)." , pinfo->ops->proctype(pinfo), (int)pid , pinfo->timeoutseq , signal_name(nsig, NULL) , nsig); if (pinfo->isapgrp && nsig > 0) { pid = -pid; } if (!(hadprivs = cl_have_full_privs())) { return_to_orig_privs(); } if (kill(pid, nsig) < 0) { if (errno == ESRCH) { /* Mission accomplished! */ cl_log(LOG_INFO, "%s process (PID %d) died before killing (try %d)" , pinfo->ops->proctype(pinfo), (int)pid , pinfo->timeoutseq); return FALSE; }else{ cl_perror("%s: kill(%d,%d) failed" , __FUNCTION__, pid, nsig); } } if (!hadprivs) { return_to_dropped_privs(); } mstimeout = pinfo->killinfo[pinfo->timeoutseq].mstimeout; pinfo->timerid = Gmain_timeout_add(mstimeout , TrackedProcTimeoutFunction , p); if (pinfo->timerid <= 0) { cl_log(LOG_ERR, "%s: Could not add new kill timer [%u]" , __FUNCTION__, pinfo->timerid); kill(pid, SIGKILL); } if (debugproctrack) { cl_log(LOG_DEBUG, "%s process (PID %d) scheduled to be killed again" " (try %d) in %ld ms [timerid %u]" , pinfo->ops->proctype(pinfo), (int)pid , pinfo->timeoutseq , mstimeout , pinfo->timerid); } return FALSE; } /* Helper struct to allow us to stuff 3 args into one ;-) */ struct prochelper { ProcTrack_ops* type; ProcTrackFun fun; void* data; }; /* Helper function to call user's function with right args... */ static void ForEachProcHelper(gpointer key, gpointer value, void * helper) { ProcTrack* p = value; struct prochelper* ph = helper; if (ph->type != NULL && ph->type != p->ops) { return; } ph->fun(p, ph->data); } /* * Iterate over the set of tracked processes. * If proctype is NULL, then walk through them all, otherwise only those * of the given type. */ void ForEachProc(ProcTrack_ops* proctype, ProcTrackFun f, void * data) { struct prochelper ph; InitProcTable(); ph.fun = f; ph.type = proctype; ph.data = data; g_hash_table_foreach(ProcessTable, ForEachProcHelper, &ph); } void DisableProcLogging() { LoggingIsEnabled = 0; } void EnableProcLogging() { LoggingIsEnabled = 1; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/realtime.c0000644000000000000000000002005612120057602026134 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /* The BSD's do not use malloc.h directly. */ /* They use stdlib.h instead */ #ifndef BSD #ifdef HAVE_MALLOC_H # include #endif #endif #include #ifdef _POSIX_MEMLOCK # include # include # include #endif #ifdef _POSIX_PRIORITY_SCHEDULING # include #endif #include #include #include #include #include #include static gboolean cl_realtimepermitted = TRUE; static void cl_rtmalloc_setup(void); #define HOGRET 0xff /* * Slightly wacko recursive function to touch requested amount * of stack so we have it pre-allocated inside our realtime code * as per suggestion from mlockall(2) */ #ifdef _POSIX_MEMLOCK static unsigned char cl_stack_hogger(unsigned char * inbuf, int kbytes) { unsigned char buf[1024]; if (inbuf == NULL) { memset(buf, HOGRET, sizeof(buf)); }else{ memcpy(buf, inbuf, sizeof(buf)); } if (kbytes > 0) { return cl_stack_hogger(buf, kbytes-1); }else{ return buf[sizeof(buf)-1]; } /* #else return HOGRET; */ } #endif /* * We do things this way to hopefully defeat "smart" malloc code which * handles large mallocs as special cases using mmap(). */ static void cl_malloc_hogger(int kbytes) { long size = kbytes * 1024; int chunksize = 1024; long nchunks = (int)(size / chunksize); int chunkbytes = nchunks * sizeof(void *); void** chunks; int j; #ifdef HAVE_MALLOPT # ifdef M_MMAP_MAX /* Keep malloc from using mmap */ mallopt(M_MMAP_MAX, 0); #endif # ifdef M_TRIM_THRESHOLD /* Keep malloc from giving memory back to the system */ mallopt(M_TRIM_THRESHOLD, -1); # endif #endif chunks=malloc(chunkbytes); if (chunks == NULL) { cl_log(LOG_INFO, "Could not preallocate (%d) bytes" , chunkbytes); return; } memset(chunks, 0, chunkbytes); for (j=0; j < nchunks; ++j) { chunks[j] = malloc(chunksize); if (chunks[j] == NULL) { cl_log(LOG_INFO, "Could not preallocate (%d) bytes" , chunksize); }else{ memset(chunks[j], 0, chunksize); } } for (j=0; j < nchunks; ++j) { if (chunks[j]) { free(chunks[j]); chunks[j] = NULL; } } free(chunks); chunks = NULL; } /* * Make us behave like a soft real-time process. * We need scheduling priority and being locked in memory. * If you ask us nicely, we'll even grow the stack and heap * for you before locking you into memory ;-). */ void cl_make_realtime(int spolicy, int priority, int stackgrowK, int heapgrowK) { #ifdef DEFAULT_REALTIME_POLICY struct sched_param sp; int staticp; #endif if (heapgrowK > 0) { cl_malloc_hogger(heapgrowK); } #ifdef _POSIX_MEMLOCK if (stackgrowK > 0) { unsigned char ret; if ((ret=cl_stack_hogger(NULL, stackgrowK)) != HOGRET) { cl_log(LOG_INFO, "Stack hogger failed 0x%x" , ret); } } #endif cl_rtmalloc_setup(); if (!cl_realtimepermitted) { cl_log(LOG_INFO , "Request to set pid %ld to realtime ignored." , (long)getpid()); return; } #ifdef DEFAULT_REALTIME_POLICY if (spolicy < 0) { spolicy = DEFAULT_REALTIME_POLICY; } if (priority <= 0) { priority = sched_get_priority_min(spolicy); } if (priority > sched_get_priority_max(spolicy)) { priority = sched_get_priority_max(spolicy); } if ((staticp=sched_getscheduler(0)) < 0) { cl_perror("unable to get scheduler parameters."); }else{ memset(&sp, 0, sizeof(sp)); sp.sched_priority = priority; if (sched_setscheduler(0, spolicy, &sp) < 0) { cl_perror("Unable to set scheduler parameters."); } } #endif #if defined _POSIX_MEMLOCK # ifdef RLIMIT_MEMLOCK # define THRESHOLD(lim) (((lim))/2) { unsigned long growsize = ((stackgrowK+heapgrowK)*1024); struct rlimit memlocklim; getrlimit(RLIMIT_MEMLOCK, &memlocklim); /* Allow for future added fields */ memlocklim.rlim_max = RLIM_INFINITY; memlocklim.rlim_cur = RLIM_INFINITY; /* Try and remove memory locking limits -- if we can */ if (setrlimit(RLIMIT_MEMLOCK, &memlocklim) < 0) { /* Didn't work - get what we can */ getrlimit(RLIMIT_MEMLOCK, &memlocklim); memlocklim.rlim_cur = memlocklim.rlim_max; setrlimit(RLIMIT_MEMLOCK, &memlocklim); } /* Could we get 'enough' ? */ /* (this is a guess - might not be right if we're not root) */ if (getrlimit(RLIMIT_MEMLOCK, &memlocklim) >= 0 && memlocklim.rlim_cur != RLIM_INFINITY && (growsize >= THRESHOLD(memlocklim.rlim_cur))) { cl_log(LOG_ERR , "Cannot lock ourselves into memory: System limits" " on locked-in memory are too small."); return; } } # endif /*RLIMIT_MEMLOCK*/ if (mlockall(MCL_CURRENT|MCL_FUTURE) >= 0) { if (ANYDEBUG) { cl_log(LOG_DEBUG, "pid %d locked in memory.", (int) getpid()); } } else if(errno == ENOSYS) { const char *err = strerror(errno); cl_log(LOG_WARNING, "Unable to lock pid %d in memory: %s", (int) getpid(), err); } else { cl_perror("Unable to lock pid %d in memory", (int) getpid()); } #endif } void cl_make_normaltime(void) { #ifdef DEFAULT_REALTIME_POLICY struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = sched_get_priority_min(SCHED_OTHER); if (sched_setscheduler(0, SCHED_OTHER, &sp) < 0) { cl_perror("unable to (re)set scheduler parameters."); } #endif #ifdef _POSIX_MEMLOCK /* Not strictly necessary. */ munlockall(); #endif } void cl_disable_realtime(void) { cl_realtimepermitted = FALSE; } void cl_enable_realtime(void) { cl_realtimepermitted = TRUE; } /* Give up the CPU for a little bit */ /* This is similar to sched_yield() but allows lower prio processes to run */ int cl_shortsleep(void) { static const struct timespec req = {0,2000001L}; return nanosleep(&req, NULL); } static int post_rt_morecore_count = 0; static unsigned long init_malloc_arena = 0L; #ifdef HAVE_MALLINFO # define MALLOC_TOTALSIZE() (((unsigned long)mallinfo().arena)+((unsigned long)mallinfo().hblkhd)) #else # define MALLOC_TOTALSIZE() (0L) #endif /* Return the number of times we went after more core */ int cl_nonrealtime_malloc_count(void) { return post_rt_morecore_count; } unsigned long cl_nonrealtime_malloc_size(void) { return (MALLOC_TOTALSIZE() - init_malloc_arena); } /* Log the number of times we went after more core */ void cl_realtime_malloc_check(void) { static int lastcount = 0; static unsigned long oldarena = 0UL; if (oldarena == 0UL) { oldarena = init_malloc_arena; } if (post_rt_morecore_count > lastcount) { if (MALLOC_TOTALSIZE() > oldarena) { cl_log(LOG_WARNING, "Performed %d more non-realtime malloc calls.", post_rt_morecore_count - lastcount); cl_log(LOG_INFO, "Total non-realtime malloc bytes: %ld", MALLOC_TOTALSIZE() - init_malloc_arena); oldarena = MALLOC_TOTALSIZE(); } lastcount = post_rt_morecore_count; } } #ifdef HAVE___DEFAULT_MORECORE static void (*our_save_morecore_hook)(void) = NULL; static void cl_rtmalloc_morecore_fun(void); static void cl_rtmalloc_morecore_fun(void) { post_rt_morecore_count++; if (our_save_morecore_hook) { our_save_morecore_hook(); } } #endif static void cl_rtmalloc_setup(void) { static gboolean inityet = FALSE; if (!inityet) { init_malloc_arena = MALLOC_TOTALSIZE(); #ifdef HAVE___DEFAULT_MORECORE our_save_morecore_hook = __after_morecore_hook; __after_morecore_hook = cl_rtmalloc_morecore_fun; inityet = TRUE; #endif } } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/replytrack.c0000644000000000000000000003630112120057602026512 0ustar00usergroup00000000000000 /* * Reply tracking library. * * Copyright (c) 2007 Alan Robertson * Author: Alan Robertson * ****************************************************************** * This library is useful for tracking replies to multicast messages * sent to cluster members. It tracks incremental membership changes * according to any desired criteria, and then keeps track of when * the last expected reply is received according to the dynamically * updated membership as of when the message was sent out. ****************************************************************** * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include /* * These are the only data items that go in our GHashTables */ struct rt_node_info { char * nodename; cl_uuid_t nodeid; }; struct node_tables { GHashTable* uuidmap; /* Keyed by uuid */ int uuidcount; GHashTable* namemap; /* Keyed by nodename*/ int namecount; }; struct _nodetrack { struct node_tables nt; int refcount; nodetrack_callback_t callback; gpointer user_data; nodetrack_callback_t extra_callback; gpointer ext_data; }; /* * Things we use to track outstanding replies * This is the structure used by the replytrack_t typedef */ struct _replytrack { replytrack_callback_t callback; gpointer user_data; unsigned timerid; struct node_tables tables; gboolean expectingmore; nodetrack_t* membership; }; struct _nodetrack_intersection { nodetrack_t** tables; int ntables; nodetrack_callback_t callback; gpointer user_data; nodetrack_t* intersection; }; static cl_uuid_t nulluuid; static int nodetrack_t_count = 0; static int replytrack_t_count = 0; static int replytrack_intersection_t_count = 0; static struct rt_node_info * rt_node_info_new(const char * nodename, cl_uuid_t nodeid) { struct rt_node_info* ret; if (!nodename) { return NULL; } ret = MALLOCT(struct rt_node_info); if (!ret) { return ret; } ret->nodename = strdup(nodename); if (!ret->nodename) { free(ret); ret = NULL; return ret; } ret->nodeid = nodeid; return ret; } static void rt_node_info_del(struct rt_node_info * ni) { if (ni != NULL) { if (ni->nodename != NULL) { free(ni->nodename); } memset(ni, 0, sizeof(*ni)); free(ni); } } /* * namehash cannot be NULL, idhash cannot be NULL, and nodename cannot be NULL * * 'id' can be a NULL uuid, in which case it goes into only the name table * 'nodename' can change over time - in which case we update our tables. * It is possible for one nodename to have more than one uuid. * We allow for that. * * Changing the uuid but keeping the nodename the same is considered to be * adding a new node with the same nodename. * Exception: A node with a null uuid is presumed to have acquired a proper * uuid if it is later seen with a non-null UUID */ static gboolean del_node_from_hashtables(struct node_tables *t , const char * nodename, cl_uuid_t id) { struct rt_node_info * entry; if (cl_uuid_is_null(&id)) { if ((entry = g_hash_table_lookup(t->namemap,nodename))!=NULL){ g_hash_table_remove(t->namemap, nodename); rt_node_info_del(entry); t->namecount--; } return TRUE; } if ((entry=g_hash_table_lookup(t->uuidmap, &id)) != NULL) { g_hash_table_remove(t->uuidmap, &id); rt_node_info_del(entry); t->uuidcount--; } return TRUE; } static gboolean add_node_to_hashtables(struct node_tables * t , const char * nodename, cl_uuid_t id) { struct rt_node_info* idinfo = NULL; if (cl_uuid_is_null(&id)) { /* Supplied uuid is the null UUID - insert in name table */ struct rt_node_info* ninfo; if (g_hash_table_lookup(t->namemap, nodename) == NULL) { if (NULL == (ninfo = rt_node_info_new(nodename, id))){ goto outofmem; } g_hash_table_insert(t->namemap,ninfo->nodename,ninfo); t->namecount++; } return TRUE; } /* Supplied uuid is not the null UUID */ if (g_hash_table_lookup(t->uuidmap,&id) == NULL) { /* See if a corresponding name is in name map */ /* If so, delete it - assume uuid was missing before */ if (g_hash_table_lookup(t->namemap, nodename) != NULL) { del_node_from_hashtables(t, nodename, nulluuid); } /* Not yet in our uuid hash table */ idinfo = rt_node_info_new(nodename, id); if (idinfo == NULL) { goto outofmem; } g_hash_table_insert(t->uuidmap, &idinfo->nodeid, idinfo); t->uuidcount++; } return TRUE; outofmem: cl_log(LOG_ERR, "%s: out of memory", __FUNCTION__); return FALSE; } static gboolean create_new_hashtables(struct node_tables*t) { t->namemap = g_hash_table_new(g_str_hash, g_str_equal); if (t->namemap == NULL) { return FALSE; } t->uuidmap = g_hash_table_new(cl_uuid_g_hash, cl_uuid_g_equal); if (t->uuidmap == NULL) { g_hash_table_destroy(t->namemap); t->namemap = NULL; return FALSE; } return TRUE; } static gboolean hashtable_destroy_rt_node_info(gpointer key, gpointer rti, gpointer unused) { rt_node_info_del(rti); rti = key = NULL; return TRUE; } static void destroy_map_hashtable(GHashTable*t) { g_hash_table_foreach_remove(t, hashtable_destroy_rt_node_info,NULL); g_hash_table_destroy(t); t = NULL; } struct tablehelp { struct node_tables* t; gboolean ret; }; static void copy_hashtables_helper(gpointer key_unused, gpointer value , gpointer user_data) { struct tablehelp * th = user_data; struct rt_node_info* ni = value; if (!add_node_to_hashtables(th->t, ni->nodename, ni->nodeid)) { th->ret = FALSE; } } static gboolean copy_hashtables(struct node_tables* tin, struct node_tables* tout) { struct tablehelp newtables; if (!create_new_hashtables(tout)){ return FALSE; } newtables.t = tout; newtables.ret = TRUE; g_hash_table_foreach(tout->namemap,copy_hashtables_helper,&newtables); if (!newtables.ret) { return FALSE; } g_hash_table_foreach(tout->uuidmap,copy_hashtables_helper,&newtables); return newtables.ret; } static gboolean mbr_inityet = FALSE; static void init_global_membership(void) { if (mbr_inityet) { return; } mbr_inityet = TRUE; memset(&nulluuid, 0, sizeof(nulluuid)); } gboolean /* Call us when an expected replier joins / comes up */ nodetrack_nodeup(nodetrack_t * mbr, const char * node, cl_uuid_t uuid) { gboolean ret; ret = add_node_to_hashtables(&mbr->nt, node, uuid); if (ret && mbr->callback) { mbr->callback(mbr, node, uuid, NODET_UP, mbr->user_data); } if (mbr->extra_callback) { mbr->extra_callback(mbr, node, uuid, NODET_UP,mbr->ext_data); } return ret; } gboolean /* Call us when an expected replier goes down / away */ nodetrack_nodedown(nodetrack_t* mbr, const char* node, cl_uuid_t uuid) { if (mbr->callback) { mbr->callback(mbr, node, uuid, NODET_DOWN, mbr->user_data); } if (mbr->extra_callback) { mbr->extra_callback(mbr, node,uuid,NODET_DOWN,mbr->ext_data); } return del_node_from_hashtables(&mbr->nt, node, uuid); } /* This function calls the user's timeout callback */ static gboolean replytrack_timeout_helper(gpointer rldata) { replytrack_t* rl = rldata; rl->expectingmore = FALSE; rl->timerid = 0; if (rl->callback) { rl->callback(rl, rl->user_data, REPLYT_TIMEOUT); } return FALSE; } replytrack_t* /* replytrack_t constructor */ replytrack_new(nodetrack_t * membership , replytrack_callback_t callback , unsigned long timeout_ms , gpointer user_data) { replytrack_t* ret = MALLOCT(replytrack_t); if (!ret) { return ret; } if (!copy_hashtables(&membership->nt, &ret->tables)) { free(ret); ret = NULL; return ret; } replytrack_t_count++; ret->membership = membership; ret->membership->refcount++; ret->callback = callback; ret->user_data = user_data; ret->expectingmore = TRUE; ret->timerid = 0; if (timeout_ms != 0 && callback != NULL) { ret->timerid = Gmain_timeout_add(timeout_ms , replytrack_timeout_helper, ret); } return ret; } void /* replytrack_t destructor */ replytrack_del(replytrack_t * rl) { rl->membership->refcount--; replytrack_t_count++; if (rl->expectingmore && rl->timerid > 0) { cl_log(LOG_INFO , "%s: destroying replytrack while still expecting" " %d replies" , __FUNCTION__ , (rl->tables.namecount + rl->tables.uuidcount)); } if (rl->timerid > 0) { g_source_remove(rl->timerid); rl->timerid = 0; } destroy_map_hashtable(rl->tables.namemap); rl->tables.namemap=NULL; destroy_map_hashtable(rl->tables.uuidmap); rl->tables.uuidmap=NULL; memset(&rl, 0, sizeof(rl)); free(rl); rl=NULL; } gboolean /* Call replytrack_gotreply when you receive an expected reply */ replytrack_gotreply(replytrack_t*rl, const char * node, cl_uuid_t uuid) { gboolean lastone; del_node_from_hashtables(&rl->tables, node, uuid); lastone = (rl->tables.namecount + rl->tables.uuidcount) == 0; if (lastone) { rl->expectingmore = FALSE; if (rl->timerid > 0) { g_source_remove(rl->timerid); rl->timerid = 0; } if (rl->callback){ rl->callback(rl, rl->user_data, REPLYT_ALLRCVD); } } return lastone; } struct replytrack_iterator_data { replytrack_t* rlist; replytrack_iterator_t f; int count; gpointer user_data; }; static void /* g_hash_table user-level iteration helper */ replytrack_iterator_helper(gpointer key_unused, gpointer entry , gpointer user_data) { struct replytrack_iterator_data* ri = user_data; struct rt_node_info* ni = entry; if (ri && ri->rlist) { ++ri->count; if (ri->f) { ri->f(ri->rlist, ri->user_data , ni->nodename, ni->nodeid); } } } int /* iterate through the outstanding expected replies */ replytrack_outstanding_iterate(replytrack_t* rl , replytrack_iterator_t i, gpointer user_data) { struct replytrack_iterator_data id; id.rlist = rl; id.f = i; id.count = 0; id.user_data = user_data; g_hash_table_foreach(rl->tables.namemap, replytrack_iterator_helper , &id); g_hash_table_foreach(rl->tables.uuidmap, replytrack_iterator_helper , &id); if (id.count != (rl->tables.namecount + rl->tables.uuidcount)) { cl_log(LOG_ERR , "%s: iteration count %d disagrees with" " (namecount %d+uuidcount %d)" , __FUNCTION__, id.count , rl->tables.namecount,rl->tables.uuidcount); } return id.count; } int /* return count of outstanding expected replies */ replytrack_outstanding_count(replytrack_t* rl) { return (rl->tables.namecount + rl->tables.uuidcount); } nodetrack_t* nodetrack_new(nodetrack_callback_t callback, gpointer user_data) { nodetrack_t* ret = MALLOCT(nodetrack_t); if (!mbr_inityet) { init_global_membership(); } if (!ret) { return ret; } nodetrack_t_count++; ret->refcount = 0; if (!create_new_hashtables(&ret->nt)) { free(ret); ret = NULL; } ret->user_data = user_data; ret->callback = callback; ret->extra_callback = NULL; ret->ext_data = NULL; return ret; } void nodetrack_del(nodetrack_t * np) { if (np->refcount) { cl_log(LOG_ERR , "%s: reply tracking reference count is %d" , __FUNCTION__, np->refcount); } nodetrack_t_count--; destroy_map_hashtable(np->nt.namemap); np->nt.namemap=NULL; destroy_map_hashtable(np->nt.uuidmap); np->nt.uuidmap=NULL; memset(np, 0, sizeof(*np)); free(np); } gboolean nodetrack_ismember(nodetrack_t* mbr, const char * name, cl_uuid_t u) { if (cl_uuid_is_null(&u)) { return(g_hash_table_lookup(mbr->nt.namemap, name) != NULL); } return (g_hash_table_lookup(mbr->nt.uuidmap, &u) != NULL); } struct nodetrack_iterator_data { nodetrack_t* rlist; nodetrack_iterator_t f; int count; gpointer user_data; }; static void /* g_hash_table user-level iteration helper */ nodetrack_iterator_helper(gpointer key_unused, gpointer entry , gpointer user_data) { struct nodetrack_iterator_data* ri = user_data; struct rt_node_info* ni = entry; if (ri && ri->rlist) { ++ri->count; if (ri->f) { ri->f(ri->rlist, ri->user_data , ni->nodename, ni->nodeid); } } } int /* iterate through the outstanding expected replies */ nodetrack_iterate(nodetrack_t* rl , nodetrack_iterator_t i, gpointer user_data) { struct nodetrack_iterator_data id; id.rlist = rl; id.f = i; id.count = 0; id.user_data = user_data; g_hash_table_foreach(rl->nt.namemap, nodetrack_iterator_helper , &id); g_hash_table_foreach(rl->nt.uuidmap, nodetrack_iterator_helper , &id); if (id.count != (rl->nt.namecount + rl->nt.uuidcount)) { cl_log(LOG_ERR , "%s: iteration count %d disagrees with" " (namecount %d+uuidcount %d)" , __FUNCTION__, id.count , rl->nt.namecount,rl->nt.uuidcount); } return id.count; } static void intersection_callback ( nodetrack_t * mbr , const char * node , cl_uuid_t u , nodetrack_change_t reason , gpointer user_data) { nodetrack_intersection_t* it = user_data; int j; gboolean allfound = TRUE; if (reason == NODET_DOWN) { if (nodetrack_ismember(it->intersection, node, u)) { nodetrack_nodedown(it->intersection,node,u); } return; } for (j=0; j < it->ntables && allfound; ++j) { if (nodetrack_ismember(it->tables[j], node, u)) { allfound = FALSE; } } if (allfound) { nodetrack_nodeup(it->intersection, node, u); } } struct li_helper { nodetrack_intersection_t* i; gboolean result; }; static void intersection_init_iterator(nodetrack_t* nt , gpointer ghelp , const char* node , cl_uuid_t uuid) { struct li_helper* help = ghelp; gboolean allfound = TRUE; int j; for (j=1; allfound && j < help->i->ntables; ++j) { if (!nodetrack_ismember(help->i->tables[j] , node, uuid)) { allfound = FALSE; } } if (allfound) { nodetrack_nodeup(help->i->intersection, node, uuid); } } nodetrack_intersection_t* nodetrack_intersection_new(nodetrack_t** tables, int ntables , nodetrack_callback_t callback, gpointer user_data) { nodetrack_intersection_t* ret; int j; ret = MALLOCT(nodetrack_intersection_t); if (!ret) { return ret; } ret->intersection = nodetrack_new(callback, user_data); if (!ret->intersection) { free(ret); ret = NULL; return ret; } ret->tables = tables; ret->ntables = ntables; ret->callback = callback; ret->user_data = user_data; for (j=0; j < ntables; ++j) { tables[j]->refcount ++; tables[j]->ext_data = ret; tables[j]->extra_callback = intersection_callback; } /* Initialize the intersection membership list */ nodetrack_iterate(tables[0], intersection_init_iterator, ret); replytrack_intersection_t_count++; return ret; } void nodetrack_intersection_del(nodetrack_intersection_t* p) { int j; for (j=0; j < p->ntables; ++j) { p->tables[j]->refcount ++; } nodetrack_del(p->intersection); p->intersection = NULL; memset(p, 0, sizeof(*p)); free(p); p = NULL; replytrack_intersection_t_count--; } nodetrack_t* nodetrack_intersection_table(nodetrack_intersection_t*p) { return p->intersection; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/setproctitle.c0000644000000000000000000001410412120057602027050 0ustar00usergroup00000000000000/* * setproctitle.c * * The code in this file, setproctitle.c is heavily based on code from * proftpd, please see the licening information below. * * This file added to the heartbeat tree by Horms * * Code to portably change the title of a programme as displayed * by ps(1). * * heartbeat: Linux-HA heartbeat code * * Copyright (C) 1999,2000,2001 Alan Robertson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu * and other respective copyright holders give permission to link this program * with OpenSSL, and distribute the resulting executable, without including * the source code for OpenSSL in the source distribution. */ #include #include #include #include #include #include #define PF_ARGV_NONE 0 #define PF_ARGV_NEW 1 #define PF_ARGV_WRITEABLE 2 #define PF_ARGV_PSTAT 3 #define PF_ARGV_PSSTRINGS 4 #if PF_ARGV_TYPE == PF_ARGV_PSTAT # include #endif #include #if PF_ARGV_TYPE != PF_ARGV_NONE static char **Argv = NULL; static char *LastArgv = NULL; #endif /* PF_ARGV_TYPE != PF_ARGV_NONE */ extern char **environ; #ifdef HAVE___PROGNAME extern char *__progname; extern char *__progname_full; #endif /* HAVE___PROGNAME */ int init_set_proc_title(int argc, char *argv[], char *envp[]) { #if PF_ARGV_TYPE == PF_ARGV_NONE return 0; #else int i; int envpsize; char **p; /* Move the environment so setproctitle can use the space. */ for(i = envpsize = 0; envp[i] != NULL; i++) { envpsize += strlen(envp[i]) + 1; } p = (char **) malloc((i + 1) * sizeof(char *)); if (p == NULL) { return -1; } environ = p; for(i = 0; envp[i] != NULL; i++) { environ[i] = strdup(envp[i]); if(environ[i] == NULL) { goto error_environ; } } environ[i] = NULL; Argv = argv; for(i = 0; i < argc; i++) { if(!i || (LastArgv + 1 == argv[i])) LastArgv = argv[i] + strlen(argv[i]); } for(i = 0; envp[i] != NULL; i++) { if((LastArgv + 1) == envp[i]) { LastArgv = envp[i] + strlen(envp[i]); } } #ifdef HAVE___PROGNAME /* Set the __progname and __progname_full variables so glibc and * company don't go nuts. - MacGyver */ __progname = strdup("heartbeat"); if (__progname == NULL) { goto error_environ; } __progname_full = strdup(argv[0]); if (__progname_full == NULL) { goto error_environ; } #endif /* HAVE___PROGNAME */ return 0; error_environ: for(i = 0; environ[i] != NULL; i++) { free(environ[i]); } free(environ); return -1; #endif /* PF_ARGV_TYPE == PF_ARGV_NONE */ } void set_proc_title(const char *fmt,...) { #if PF_ARGV_TYPE != PF_ARGV_NONE va_list msg; static char statbuf[BUFSIZ]; #ifndef HAVE_SETPROCTITLE #if PF_ARGV_TYPE == PF_ARGV_PSTAT union pstun pst; #endif /* PF_ARGV_PSTAT */ int i,maxlen = (LastArgv - Argv[0]) - 2; char *p; #endif /* HAVE_SETPROCTITLE */ va_start(msg,fmt); memset(statbuf, 0, sizeof(statbuf)); #ifdef HAVE_SETPROCTITLE # if __FreeBSD__ >= 4 && !defined(FREEBSD4_0) && !defined(FREEBSD4_1) /* FreeBSD's setproctitle() automatically prepends the process name. */ vsnprintf(statbuf, sizeof(statbuf), fmt, msg); # else /* FREEBSD4 */ /* Manually append the process name for non-FreeBSD platforms. */ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf), fmt, msg); # endif /* FREEBSD4 */ setproctitle("%s", statbuf); #else /* HAVE_SETPROCTITLE */ /* Manually append the process name for non-setproctitle() platforms. */ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf), fmt, msg); #endif /* HAVE_SETPROCTITLE */ va_end(msg); #ifdef HAVE_SETPROCTITLE return; #else i = strlen(statbuf); #if PF_ARGV_TYPE == PF_ARGV_NEW /* We can just replace argv[] arguments. Nice and easy. */ Argv[0] = statbuf; Argv[1] = NULL; #endif /* PF_ARGV_NEW */ #if PF_ARGV_TYPE == PF_ARGV_WRITEABLE /* We can overwrite individual argv[] arguments. Semi-nice. */ snprintf(Argv[0], maxlen, "%s", statbuf); p = &Argv[0][i]; while(p < LastArgv) *p++ = '\0'; Argv[1] = NULL; #endif /* PF_ARGV_WRITEABLE */ #if PF_ARGV_TYPE == PF_ARGV_PSTAT pst.pst_command = statbuf; pstat(PSTAT_SETCMD, pst, i, 0, 0); #endif /* PF_ARGV_PSTAT */ #if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = statbuf; #endif /* PF_ARGV_PSSTRINGS */ #endif /* HAVE_SETPROCTITLE */ #endif /* PF_ARGV_TYPE != PF_ARGV_NONE */ } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/timers.c0000644000000000000000000000546012120057602025637 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include int setmsrepeattimer(long ms) { long secs = ms / 1000L; long usecs = (ms % 1000L)*1000L; struct itimerval nexttime = { {secs, usecs} /* Repeat Interval */ , {secs, usecs} /* Timer Value */ }; #if 0 cl_log(LOG_DEBUG, "Setting repeating timer for %ld ms" , ms); #endif /* Is this right??? */ CL_IGNORE_SIG(SIGALRM); return setitimer(ITIMER_REAL, &nexttime, NULL); } int setmsalarm(long ms) { long secs = ms / 1000L; long usecs = (ms % 1000L)*1000L; struct itimerval nexttime = { {0L, 0L} /* Repeat Interval */ , {secs, usecs} /* Timer Value */ }; return setitimer(ITIMER_REAL, &nexttime, NULL); } int cancelmstimer(void) { struct itimerval nexttime = { {0L, 0L} /* Repeat Interval */ , {0L, 0L} /* Timer Value */ }; return setitimer(ITIMER_REAL, &nexttime, NULL); } static int alarmpopped = 0; static void st_timer_handler(int nsig) { ++alarmpopped; } /* * Pretty simple: * 1) Set up SIGALRM signal handler * 2) set alarmpopped to FALSE; * 2) Record current time * 3) Call setmsalarm(ms) * 4) Call pause(2) * 5) Call cancelmstimer() * 6) Reset signal handler * 7) See if SIGALRM happened * if so: return zero * if not: get current time, and compute milliseconds left 'til signal * should arrive, and return that... */ long mssleep(long ms) { struct sigaction saveaction; longclock_t start; longclock_t finish; unsigned long elapsedms; memset(&saveaction, 0, sizeof(saveaction)); cl_signal_set_simple_handler(SIGALRM, st_timer_handler, &saveaction); alarmpopped = 0; start = time_longclock(); setmsalarm(ms); pause(); cancelmstimer(); cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction); if (alarmpopped) { return 0; } finish = time_longclock(); elapsedms = longclockto_ms(sub_longclock(finish, start)); return ms - elapsedms; } Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/transient-test.sh0000755000000000000000000000567012120057602027516 0ustar00usergroup00000000000000#!/bin/sh # # 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 St, Fifth Floor, Boston, MA 02110-1301 USA # # ####FIXME # Known problems within this testing: # 1. Doesn't reflect "someone else's message" problems into error count. # 2. Path to "ipctransient{client,server}" not flexible enough. no_logs=0 exit_on_error=1 num_servers=10 num_clients=10 client_time=2 commpath_args="" cmd=`basename $0` USAGE="Usage: $cmd [-c clients] [-s servers] [-t timetowait] [-C commpath]" while getopts c:s:C:t: c do case $c in c) num_clients=$OPTARG ;; s) num_servers=$OPTARG ;; t) client_time=$OPTARG ;; C) commpath_args="-$c $OPTARG" ;; \?) echo $USAGE exit 2 ;; esac done shift `expr $OPTIND - 1` total_failed=0 server_failed=0 server_loop_cnt=0 while [ $server_loop_cnt != $num_servers ]; do echo "############ DEBUG: Starting server iter $server_loop_cnt" if [ $no_logs = 1 ]; then ./ipctransientserver $commpath_args > /dev/null 2>&1 & else ./ipctransientserver $commpath_args & fi server_pid=$! sleep 5 client_failed=0 client_loop_cnt=0 while [ $client_loop_cnt != $num_clients ]; do sleep 5 echo "############ DEBUG: Starting client iter $client_loop_cnt" if [ $no_logs = 1 ]; then ./ipctransientclient $commpath_args > /dev/null 2>&1 & else ./ipctransientclient $commpath_args & fi client_pid=$! sleep $client_time if [ $exit_on_error = 1 ];then kill -0 $client_pid > /dev/null 2>&1 else kill -9 $client_pid > /dev/null 2>&1 fi rc=$? if [ $rc = 0 ]; then echo "############ ERROR: Iter $client_loop_cnt failed to receive all messages" client_failed=`expr $client_failed + 1` if [ $exit_on_error = 1 ];then echo "terminating after first error..." exit 0 fi else echo "############ INFO: Iter $client_loop_cnt passed" fi client_loop_cnt=`expr $client_loop_cnt + 1`; done server_loop_cnt=`expr $server_loop_cnt + 1`; total_failed=`expr $total_failed + $client_failed` kill -9 $server_pid > /dev/null 2>&1 rc=$? if [ $rc = 0 ]; then echo "############ ERROR: Server was already dead" server_failed=`expr $server_failed + 1` fi done total_failed=`expr $total_failed + $server_failed` if [ $total_failed = 0 ]; then echo "INFO: All tests passed" else echo "ERROR: $total_failed tests failed" fi exit $total_failed Reusable-Cluster-Components-glue--3cff550e1084/lib/clplumbing/uids.c0000644000000000000000000000633412120057602025301 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #define NOBODY "nobody" #if defined(HAVE_SETEUID) && defined(HAVE_SETEGID) && \ defined(_POSIX_SAVED_IDS) # define CAN_DROP_PRIVS 1 #endif #ifndef CAN_DROP_PRIVS int drop_privs(uid_t uid, gid_t gid) { return 0; } int return_to_orig_privs(void) { return 0; } int return_to_dropped_privs(void) { return 0; } int cl_have_full_privs(void) { return 0; } #else static int anysaveduid = 0; static uid_t nobodyuid=-1; static gid_t nobodygid=-1; static uid_t poweruid=-1; static gid_t powergid=-1; static int privileged_state = 1; /* WARNING: uids are unsigned! */ #define WANT_NOBODY(uid) ((uid) == 0) int /* Become nobody - and remember our original privileges */ drop_privs(uid_t uid, gid_t gid) { int rc; gid_t curgid = getgid(); if (!anysaveduid) { poweruid=getuid(); powergid=curgid; } if (WANT_NOBODY(uid)) { struct passwd* p; p = getpwnam(NOBODY); if (p == NULL) { return -1; } uid = p->pw_uid; gid = p->pw_gid; } if (setegid(gid) < 0) { return -1; } rc = seteuid(uid); if (rc >= 0) { anysaveduid = 1; nobodyuid=uid; nobodygid=gid; privileged_state = 0; }else{ /* Attempt to recover original privileges */ int err = errno; setegid(curgid); errno = err; } cl_untaint_coredumps(); return rc; } int /* Return to our original privileges (if any) */ return_to_orig_privs(void) { int rc; if (!anysaveduid) { return 0; } if (seteuid(poweruid) < 0) { return -1; } privileged_state = 1; rc = setegid(powergid); /* * Sad but true, for security reasons we can't call * cl_untaint_coredumps() here - because it might cause an * leak of confidential information for some applications. * So, the applications need to use either cl_untaint_coredumps() * when they change privileges, or they need to call * cl_set_all_coredump_signal_handlers() to handle core dump * signals and set their privileges to maximum before core * dumping. See the comments in coredumps.c for more details. */ return rc; } int /* Return to "nobody" level of privs (if any) */ return_to_dropped_privs(void) { int rc; if (!anysaveduid) { return 0; } setegid(nobodygid); privileged_state = 0; rc = seteuid(nobodyuid); /* See note above about dumping core */ return rc; } /* Return TRUE if we have full privileges at the moment */ int cl_have_full_privs(void) { return privileged_state != 0; } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/lrm/Makefile.am0000644000000000000000000000260312120057602024656 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl lrmdir = $(localstatedir)/lib/heartbeat/lrm COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \ $(GLIBLIB) lib_LTLIBRARIES = liblrm.la liblrm_la_SOURCES = lrm_msg.c clientlib.c racommon.c liblrm_la_LDFLAGS = -version-info 2:0:0 $(COMMONLIBS) liblrm_la_CFLAGS = $(INCLUDES) install-exec-local: $(mkinstalldirs) $(DESTDIR)$(lrmdir) -chgrp $(GLUE_DAEMON_GROUP) $(DESTDIR)/$(lrmdir) chmod 770 $(DESTDIR)/$(lrmdir) Reusable-Cluster-Components-glue--3cff550e1084/lib/lrm/clientlib.c0000644000000000000000000011602712120057602024741 0ustar00usergroup00000000000000/* * Client Library for Local Resource Manager API. * * Author: Huang Zhen * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include /* FIXME: Notice: this define should be replaced when merge to the whole pkg*/ #define LRM_MAXPIDLEN 256 #define LRM_ID "lrm" #define LOG_FAIL_create_lrm_msg(msg_type) \ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \ "function create_lrm_msg." \ , __FUNCTION__, __LINE__, msg_type) #define LOG_FAIL_create_lrm_rsc_msg(msg_type) \ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \ "function create_lrm_rsc_msg." \ , __FUNCTION__, __LINE__, msg_type) #define LOG_FAIL_receive_reply(msg_type) \ cl_log(LOG_ERR, "%s(%d): failed to receive a reply message of %s." \ , __FUNCTION__, __LINE__, msg_type) #define LOG_FAIL_SEND_MSG(msg_type, chan_name) \ cl_log(LOG_ERR, "%s(%d): failed to send a %s message to lrmd " \ "via %s channel." \ , __FUNCTION__, __LINE__, msg_type, chan_name) #define LOG_GOT_FAIL_RET(priority, msg_type) \ cl_log(priority, "%s(%d): got a return code HA_FAIL from " \ "a reply message of %s with function get_ret_from_msg." \ , __FUNCTION__, __LINE__, msg_type) #define LOG_BASIC_ERROR(apiname) \ cl_log(LOG_ERR, "%s(%d): %s failed." \ , __FUNCTION__, __LINE__, apiname) #define LOG_FAIL_GET_MSG_FIELD(priority, field_name, msg) \ {cl_log(priority, "%s(%d): failed to get the value " \ "of field %s from a ha_msg" \ , __FUNCTION__, __LINE__, field_name); \ cl_log(LOG_INFO, "%s: Message follows:", __FUNCTION__); \ cl_log_message(LOG_INFO, (msg)); \ } /* declare the functions used by the lrm_ops structure*/ static int lrm_signon (ll_lrm_t* lrm, const char * app_name); static int lrm_signoff (ll_lrm_t*); static int lrm_delete (ll_lrm_t*); static int lrm_set_lrm_callback (ll_lrm_t* lrm, lrm_op_done_callback_t op_done_callback_func); static GList* lrm_get_rsc_class_supported (ll_lrm_t* lrm); static GList* lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* class); static GList* lrm_get_rsc_provider_supported (ll_lrm_t* lrm ,const char* class, const char* type); static char* lrm_get_rsc_type_metadata(ll_lrm_t* lrm, const char* class ,const char* type, const char* provider); static GHashTable* lrm_get_all_type_metadata(ll_lrm_t*, const char* class); static GList* lrm_get_all_rscs (ll_lrm_t* lrm); static lrm_rsc_t* lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id); static int lrm_add_rsc (ll_lrm_t*, const char* id, const char* class ,const char* type, const char* provider ,GHashTable* parameter); static int lrm_delete_rsc (ll_lrm_t*, const char* id); static int lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc ,const char* fail_reason); static int lrm_set_lrmd_param (ll_lrm_t* lrm, const char* name, const char *value); static char* lrm_get_lrmd_param (ll_lrm_t* lrm, const char* name); static IPC_Channel* lrm_ipcchan (ll_lrm_t*); static int lrm_msgready (ll_lrm_t*); static int lrm_rcvmsg (ll_lrm_t*, int blocking); static struct lrm_ops lrm_ops_instance = { lrm_signon, lrm_signoff, lrm_delete, lrm_set_lrm_callback, lrm_set_lrmd_param, lrm_get_lrmd_param, lrm_get_rsc_class_supported, lrm_get_rsc_type_supported, lrm_get_rsc_provider_supported, lrm_get_rsc_type_metadata, lrm_get_all_type_metadata, lrm_get_all_rscs, lrm_get_rsc, lrm_add_rsc, lrm_delete_rsc, lrm_fail_rsc, lrm_ipcchan, lrm_msgready, lrm_rcvmsg }; /* declare the functions used by the lrm_rsc_ops structure*/ static int rsc_perform_op (lrm_rsc_t*, lrm_op_t* op); static int rsc_cancel_op (lrm_rsc_t*, int call_id); static int rsc_flush_ops (lrm_rsc_t*); static GList* rsc_get_cur_state (lrm_rsc_t*, state_flag_t* cur_state); static lrm_op_t* rsc_get_last_result (lrm_rsc_t*, const char* op_type); static gint compare_call_id(gconstpointer a, gconstpointer b); static struct rsc_ops rsc_ops_instance = { rsc_perform_op, rsc_cancel_op, rsc_flush_ops, rsc_get_cur_state, rsc_get_last_result }; /* define the internal data used by the client library*/ static int is_signed_on = FALSE; static IPC_Channel* ch_cmd = NULL; static IPC_Channel* ch_cbk = NULL; static lrm_op_done_callback_t op_done_callback = NULL; /* define some utility functions*/ static int get_ret_from_ch(IPC_Channel* ch); static int get_ret_from_msg(struct ha_msg* msg); static struct ha_msg* op_to_msg (lrm_op_t* op); static lrm_op_t* msg_to_op(struct ha_msg* msg); static void free_op (lrm_op_t* op); /* define of the api functions*/ ll_lrm_t* ll_lrm_new (const char * llctype) { ll_lrm_t* lrm; /* check the parameter*/ if (0 != STRNCMP_CONST(llctype, LRM_ID)) { cl_log(LOG_ERR, "ll_lrm_new: wrong parameter"); return NULL; } /* alloc memory for lrm*/ if (NULL == (lrm = (ll_lrm_t*) g_new(ll_lrm_t,1))) { cl_log(LOG_ERR, "ll_lrm_new: can not allocate memory"); return NULL; } /* assign the ops*/ lrm->lrm_ops = &lrm_ops_instance; return lrm; } static int lrm_signon (ll_lrm_t* lrm, const char * app_name) { GHashTable* ch_cmd_attrs; GHashTable* ch_cbk_attrs; struct ha_msg* msg; char path[] = IPC_PATH_ATTR; char cmd_path[] = LRM_CMDPATH; char callback_path[] = LRM_CALLBACKPATH; /* check parameters*/ if (NULL == lrm || NULL == app_name) { cl_log(LOG_ERR, "lrm_signon: wrong parameter"); return HA_FAIL; } /* if already signed on, sign off first*/ if (is_signed_on) { cl_log(LOG_WARNING, "lrm_signon: the client is alreay signed on, re-sign"); lrm_signoff(lrm); } /* create the command ipc channel to lrmd*/ ch_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(ch_cmd_attrs, path, cmd_path); ch_cmd = ipc_channel_constructor(IPC_ANYTYPE, ch_cmd_attrs); g_hash_table_destroy(ch_cmd_attrs); if (NULL == ch_cmd){ lrm_signoff(lrm); cl_log(LOG_WARNING, "lrm_signon: can not connect to lrmd for cmd channel"); return HA_FAIL; } if (IPC_OK != ch_cmd->ops->initiate_connection(ch_cmd)) { lrm_signoff(lrm); cl_log(LOG_WARNING, "lrm_signon: can not initiate connection"); return HA_FAIL; } /* construct the reg msg*/ if (NULL == (msg = create_lrm_reg_msg(app_name))) { lrm_signoff(lrm); cl_log(LOG_ERR,"lrm_signon: failed to create a register message"); return HA_FAIL; } /* send the msg*/ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { lrm_signoff(lrm); ha_msg_del(msg); LOG_FAIL_SEND_MSG(REGISTER, "ch_cmd"); return HA_FAIL; } /* parse the return msg*/ if (HA_OK != get_ret_from_ch(ch_cmd)) { ha_msg_del(msg); lrm_signoff(lrm); LOG_FAIL_receive_reply(REGISTER); return HA_FAIL; } /* create the callback ipc channel to lrmd*/ ch_cbk_attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(ch_cbk_attrs, path, callback_path); ch_cbk = ipc_channel_constructor(IPC_ANYTYPE,ch_cbk_attrs); g_hash_table_destroy(ch_cbk_attrs); if (NULL == ch_cbk) { ha_msg_del(msg); lrm_signoff(lrm); cl_log(LOG_ERR, "lrm_signon: failed to construct a callback " "channel to lrmd"); return HA_FAIL; } if (IPC_OK != ch_cbk->ops->initiate_connection(ch_cbk)) { ha_msg_del(msg); lrm_signoff(lrm); cl_log(LOG_ERR, "lrm_signon: failed to initiate the callback channel."); return HA_FAIL; } /* send the msg*/ if (HA_OK != msg2ipcchan(msg,ch_cbk)) { lrm_signoff(lrm); ha_msg_del(msg); LOG_FAIL_SEND_MSG(REGISTER, "ch_cbk"); return HA_FAIL; } ha_msg_del(msg); /* parse the return msg*/ if (HA_OK != get_ret_from_ch(ch_cbk)) { lrm_signoff(lrm); LOG_FAIL_receive_reply(REGISTER); return HA_FAIL; } /* ok, we sign on sucessfully now*/ is_signed_on = TRUE; return HA_OK; } static int lrm_signoff (ll_lrm_t* lrm) { /* close channels */ if (NULL != ch_cmd) { if (IPC_ISWCONN(ch_cmd)) { ch_cmd->ops->destroy(ch_cmd); } ch_cmd = NULL; } if (NULL != ch_cbk) { if (IPC_ISWCONN(ch_cbk)) { ch_cbk->ops->destroy(ch_cbk); } ch_cbk = NULL; } is_signed_on = FALSE; return HA_OK; } static int lrm_delete (ll_lrm_t* lrm) { /* check the parameter */ if (NULL == lrm) { cl_log(LOG_ERR,"lrm_delete: the parameter is a null pointer."); return HA_FAIL; } g_free(lrm); return HA_OK; } static int lrm_set_lrm_callback (ll_lrm_t* lrm, lrm_op_done_callback_t op_done_callback_func) { op_done_callback = op_done_callback_func; return HA_OK; } static GList* lrm_get_rsc_class_supported (ll_lrm_t* lrm) { struct ha_msg* msg; struct ha_msg* ret; GList* class_list = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_rsc_class_supported: ch_cmd is a null pointer."); return NULL; } /* create the get ra type message */ msg = create_lrm_msg(GETRSCCLASSES); if ( NULL == msg) { LOG_FAIL_create_lrm_msg(GETRSCCLASSES); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETRSCCLASSES, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return message */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETRSCCLASSES); return NULL; } /* get the return code of the message */ if (HA_OK != get_ret_from_msg(ret)) { LOG_GOT_FAIL_RET(LOG_WARNING, GETRSCCLASSES); ha_msg_del(ret); return NULL; } /* get the ra type list from message */ class_list = ha_msg_value_str_list(ret,F_LRM_RCLASS); ha_msg_del(ret); return class_list; } static GList* lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* rclass) { struct ha_msg* msg; struct ha_msg* ret; GList* type_list = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "%s(%d): ch_cmd is null." , __FUNCTION__, __LINE__); return NULL; } /* create the get ra type message */ msg = create_lrm_msg(GETRSCTYPES); if ( NULL == msg) { LOG_FAIL_create_lrm_msg(GETRSCTYPES); return NULL; } if ( HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETRSCTYPES, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return message */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETRSCTYPES); return NULL; } /* get the return code of the message */ if (HA_OK != get_ret_from_msg(ret)) { LOG_GOT_FAIL_RET(LOG_ERR, GETRSCTYPES); ha_msg_del(ret); return NULL; } /* get the ra type list from message */ type_list = ha_msg_value_str_list(ret,F_LRM_RTYPES); ha_msg_del(ret); return type_list; } static GList* lrm_get_rsc_provider_supported (ll_lrm_t* lrm, const char* class, const char* type) { struct ha_msg* msg; struct ha_msg* ret; GList* provider_list = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_rsc_provider_supported: ch_mod is null."); return NULL; } /* create the get ra providers message */ msg = create_lrm_msg(GETPROVIDERS); if ( NULL == msg) { LOG_FAIL_create_lrm_msg(GETPROVIDERS); return NULL; } if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class) || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETPROVIDERS, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return message */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETPROVIDERS); return NULL; } /* get the return code of the message */ if (HA_OK != get_ret_from_msg(ret)) { LOG_GOT_FAIL_RET(LOG_ERR, GETPROVIDERS); ha_msg_del(ret); return NULL; } /* get the ra provider list from message */ provider_list = ha_msg_value_str_list(ret,F_LRM_RPROVIDERS); ha_msg_del(ret); return provider_list; } /* * lrm_get_all_type_metadatas(): * The key of the hash table is in the format "type:provider" * The value of the hash table is the metadata. */ static GHashTable* lrm_get_all_type_metadata (ll_lrm_t* lrm, const char* rclass) { GHashTable* metas = g_hash_table_new_full(g_str_hash, g_str_equal , g_free, g_free); GList* types = lrm_get_rsc_type_supported (lrm, rclass); GList* providers = NULL; GList* cur_type = NULL; GList* cur_provider = NULL; cur_type = g_list_first(types); while (cur_type != NULL) { const char* type; char key[MAXLENGTH]; type = (const char*) cur_type->data; providers = lrm_get_rsc_provider_supported(lrm, rclass, type); cur_provider = g_list_first(providers); while (cur_provider != NULL) { const char* meta; const char* provider; provider = (const char*) cur_provider->data; meta = lrm_get_rsc_type_metadata(lrm,rclass,type,provider); if (NULL == meta) { cur_provider = g_list_next(cur_provider); continue; } snprintf(key,MAXLENGTH, "%s:%s",type,provider); key[MAXLENGTH-1]='\0'; g_hash_table_insert(metas,g_strdup(key),g_strdup(meta)); cur_provider = g_list_next(cur_provider); } lrm_free_str_list(providers); cur_type=g_list_next(cur_type); } lrm_free_str_list(types); return metas; } static char* lrm_get_rsc_type_metadata (ll_lrm_t* lrm, const char* rclass, const char* rtype, const char* provider) { struct ha_msg* msg; struct ha_msg* ret; const char* tmp = NULL; char* metadata = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_rsc_type_metadata: ch_mod is null."); return NULL; } /* create the get ra type message */ msg = create_lrm_msg(GETRSCMETA); if (NULL == msg ) { LOG_FAIL_create_lrm_msg(GETRSCMETA); return NULL; } if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass) || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, rtype)){ ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } if( provider ) { if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) { LOG_BASIC_ERROR("ha_msg_add"); ha_msg_del(msg); return NULL; } } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETRSCMETA, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return message */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETRSCMETA); return NULL; } /* get the return code of the message */ if (HA_OK != get_ret_from_msg(ret)) { LOG_GOT_FAIL_RET(LOG_ERR, GETRSCMETA); ha_msg_del(ret); return NULL; } /* get the metadata from message */ tmp = cl_get_string(ret, F_LRM_METADATA); if (NULL!=tmp) { metadata = g_strdup(tmp); } ha_msg_del(ret); return metadata; } static GList* lrm_get_all_rscs (ll_lrm_t* lrm) { struct ha_msg* msg = NULL; struct ha_msg* ret = NULL; GList* rid_list = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_all_rscs: ch_mod is null."); return NULL; } /* create the msg of get all resource */ msg = create_lrm_msg(GETALLRCSES); if ( NULL == msg) { LOG_FAIL_create_lrm_msg(GETALLRCSES); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETALLRCSES, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return msg */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETALLRCSES); return NULL; } /* get the return code of msg */ if (HA_OK != get_ret_from_msg(ret)) { LOG_GOT_FAIL_RET(LOG_ERR, GETALLRCSES); ha_msg_del(ret); return NULL; } /* get the rsc_id list from msg */ rid_list = ha_msg_value_str_list(ret,F_LRM_RID); ha_msg_del(ret); /* return the id list */ return rid_list; } static lrm_rsc_t* lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id) { struct ha_msg* msg = NULL; struct ha_msg* ret = NULL; lrm_rsc_t* rsc = NULL; /* check whether the rsc_id is available */ if (strlen(rsc_id) >= RID_LEN) { cl_log(LOG_ERR, "lrm_get_rsc: rsc_id is too long."); return NULL; } /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null."); return NULL; } /* create the msg of get resource */ msg = create_lrm_rsc_msg(rsc_id, GETRSC); if ( NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(GETRSC); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETRSC, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return msg from lrmd */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETRSC); return NULL; } /* get the return code of return message */ if (HA_OK != get_ret_from_msg(ret)) { ha_msg_del(ret); return NULL; } /* create a new resource structure */ rsc = g_new(lrm_rsc_t, 1); /* fill the field of resource with the data from msg */ rsc->id = g_strdup(ha_msg_value(ret, F_LRM_RID)); rsc->type = g_strdup(ha_msg_value(ret, F_LRM_RTYPE)); rsc->class = g_strdup(ha_msg_value(ret, F_LRM_RCLASS)); rsc->provider = g_strdup(ha_msg_value(ret, F_LRM_RPROVIDER)); rsc->params = ha_msg_value_str_table(ret,F_LRM_PARAM); rsc->ops = &rsc_ops_instance; ha_msg_del(ret); /* return the new resource */ return rsc; } static int lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc , const char* fail_reason) { struct ha_msg* msg; /* check whether the rsc_id is available */ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) { cl_log(LOG_ERR, "%s: wrong parameter rsc_id.", __FUNCTION__); return HA_FAIL; } /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__); return HA_FAIL; } /* create the message */ msg = create_lrm_rsc_msg(rsc_id,FAILRSC); if (NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(FAILRSC); return HA_FAIL; } if ((fail_reason && HA_OK != ha_msg_add(msg,F_LRM_FAIL_REASON,fail_reason)) || HA_OK != ha_msg_add_int(msg, F_LRM_ASYNCMON_RC, fail_rc) ) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return HA_FAIL; } /* send to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); /* check the result */ if (HA_OK != get_ret_from_ch(ch_cmd)) { LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC); return HA_FAIL; } return HA_OK; } static int lrm_set_lrmd_param(ll_lrm_t* lrm, const char* name, const char *value) { struct ha_msg* msg; if (!name || !value) { cl_log(LOG_ERR, "%s: no parameter name or value", __FUNCTION__); return HA_FAIL; } /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__); return HA_FAIL; } /* create the message */ msg = create_lrm_msg(SETLRMDPARAM); if (NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(SETLRMDPARAM); return HA_FAIL; } if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name) || HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_VAL,value)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return HA_FAIL; } /* send to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); /* check the result */ if (HA_OK != get_ret_from_ch(ch_cmd)) { LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC); return HA_FAIL; } return HA_OK; } static char* lrm_get_lrmd_param (ll_lrm_t* lrm, const char *name) { struct ha_msg* msg = NULL; struct ha_msg* ret = NULL; const char* value = NULL; char* v2; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null."); return NULL; } /* create the msg of get resource */ msg = create_lrm_msg(GETLRMDPARAM); if ( NULL == msg) { LOG_FAIL_create_lrm_msg(GETLRMDPARAM); return NULL; } if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETLRMDPARAM, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return msg from lrmd */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETLRMDPARAM); return NULL; } /* get the return code of return message */ if (HA_OK != get_ret_from_msg(ret)) { ha_msg_del(ret); return NULL; } value = ha_msg_value(ret,F_LRM_LRMD_PARAM_VAL); if (!value) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_LRMD_PARAM_VAL, ret); ha_msg_del(ret); return NULL; } v2 = g_strdup(value); ha_msg_del(ret); return v2; } static int lrm_add_rsc (ll_lrm_t* lrm, const char* rsc_id, const char* class , const char* type, const char* provider, GHashTable* parameter) { struct ha_msg* msg; /* check whether the rsc_id is available */ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) { cl_log(LOG_ERR, "lrm_add_rsc: wrong parameter rsc_id."); return HA_FAIL; } /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_add_rsc: ch_mod is null."); return HA_FAIL; } /* create the message of add resource */ msg = create_lrm_addrsc_msg(rsc_id, class, type, provider, parameter); if ( NULL == msg) { cl_log(LOG_ERR, "%s(%d): failed to create a ADDSRC message " "with function create_lrm_addrsc_msg" , __FUNCTION__, __LINE__); return HA_FAIL; } /* send to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(ADDRSC, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); /* check the result */ if (HA_OK != get_ret_from_ch(ch_cmd)) { LOG_GOT_FAIL_RET(LOG_ERR, ADDRSC); return HA_FAIL; } return HA_OK; } static int lrm_delete_rsc (ll_lrm_t* lrm, const char* rsc_id) { struct ha_msg* msg = NULL; int rc; /* check whether the rsc_id is available */ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) { cl_log(LOG_ERR, "lrm_delete_rsc: wrong parameter rsc_id."); return HA_FAIL; } /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "lrm_delete_rsc: ch_mod is null."); return HA_FAIL; } /* create the msg of del resource */ msg = create_lrm_rsc_msg(rsc_id, DELRSC); if ( NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(DELRSC); return HA_FAIL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(DELRSC, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); /* check the response of the msg */ rc = get_ret_from_ch(ch_cmd); if (rc != HA_OK && rc != HA_RSCBUSY) { LOG_GOT_FAIL_RET(LOG_ERR, DELRSC); return HA_FAIL; } return rc; } static IPC_Channel* lrm_ipcchan (ll_lrm_t* lrm) { if (NULL == ch_cbk) { cl_log(LOG_ERR, "lrm_inputfd: callback channel is null."); return NULL; } return ch_cbk; } static gboolean lrm_msgready (ll_lrm_t* lrm) { if (NULL == ch_cbk) { cl_log(LOG_ERR, "lrm_msgready: callback channel is null."); return FALSE; } return ch_cbk->ops->is_message_pending(ch_cbk); } static int lrm_rcvmsg (ll_lrm_t* lrm, int blocking) { struct ha_msg* msg = NULL; lrm_op_t* op = NULL; int msg_count = 0; /* if it is not blocking mode and no message in the channel, return */ if ((!lrm_msgready(lrm)) && (!blocking)) { cl_log(LOG_DEBUG, "lrm_rcvmsg: no message and non-block."); return msg_count; } /* wait until message ready */ if (!lrm_msgready(lrm)) { ch_cbk->ops->waitin(ch_cbk); } while (lrm_msgready(lrm)) { if (ch_cbk->ch_status == IPC_DISCONNECT) { return msg_count; } /* get the message */ msg = msgfromIPC(ch_cbk, MSG_ALLOWINTR); if (msg == NULL) { cl_log(LOG_WARNING, "%s(%d): receive a null message with msgfromIPC." , __FUNCTION__, __LINE__); return msg_count; } msg_count++; op = msg_to_op(msg); if (NULL!=op && NULL!=op_done_callback) { (*op_done_callback)(op); } free_op(op); ha_msg_del(msg); } return msg_count; } /* following are the functions for rsc_ops */ static int rsc_perform_op (lrm_rsc_t* rsc, lrm_op_t* op) { int rc = 0; struct ha_msg* msg = NULL; char* rsc_id; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd || NULL == rsc || NULL == rsc->id || NULL == op || NULL == op->op_type) { cl_log(LOG_ERR, "rsc_perform_op: wrong parameters."); return HA_FAIL; } /* create the msg of perform op */ rsc_id = op->rsc_id; op->rsc_id = rsc->id; msg = op_to_msg(op); op->rsc_id = rsc_id; if ( NULL == msg) { cl_log(LOG_ERR, "rsc_perform_op: failed to create a message " "with function op_to_msg"); return HA_FAIL; } /* send it to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(PERFORMOP, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); /* check return code, the return code is the call_id of the op */ rc = get_ret_from_ch(ch_cmd); return rc; } static int rsc_cancel_op (lrm_rsc_t* rsc, int call_id) { int rc; struct ha_msg* msg = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "rsc_cancel_op: ch_mod is null."); return HA_FAIL; } /* check parameter */ if (NULL == rsc) { cl_log(LOG_ERR, "rsc_cancel_op: parameter rsc is null."); return HA_FAIL; } /* create the msg of flush ops */ msg = create_lrm_rsc_msg(rsc->id,CANCELOP); if (NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(CANCELOP); return HA_FAIL; } if (HA_OK != ha_msg_add_int(msg, F_LRM_CALLID, call_id)) { LOG_BASIC_ERROR("ha_msg_add_int"); ha_msg_del(msg); return HA_FAIL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(CANCELOP, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); rc = get_ret_from_ch(ch_cmd); return rc; } static int rsc_flush_ops (lrm_rsc_t* rsc) { int rc; struct ha_msg* msg = NULL; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "rsc_flush_ops: ch_mod is null."); return HA_FAIL; } /* check parameter */ if (NULL == rsc) { cl_log(LOG_ERR, "rsc_flush_ops: parameter rsc is null."); return HA_FAIL; } /* create the msg of flush ops */ msg = create_lrm_rsc_msg(rsc->id,FLUSHOPS); if ( NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(CANCELOP); return HA_FAIL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(FLUSHOPS, "ch_cmd"); return HA_FAIL; } ha_msg_del(msg); rc = get_ret_from_ch(ch_cmd); return rc>0?rc:HA_FAIL; } static gint compare_call_id(gconstpointer a, gconstpointer b) { const lrm_op_t* opa = (const lrm_op_t*)a; const lrm_op_t* opb = (const lrm_op_t*)b; return opa->call_id - opb->call_id; } static GList* rsc_get_cur_state (lrm_rsc_t* rsc, state_flag_t* cur_state) { GList* op_list = NULL, * tmplist = NULL; struct ha_msg* msg = NULL; struct ha_msg* ret = NULL; struct ha_msg* op_msg = NULL; lrm_op_t* op = NULL; int state; int op_count, i; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "rsc_get_cur_state: ch_mod is null."); return NULL; } /* check paramter */ if (NULL == rsc) { cl_log(LOG_ERR, "rsc_get_cur_state: parameter rsc is null."); return NULL; } /* create the msg of get current state of resource */ msg = create_lrm_rsc_msg(rsc->id,GETRSCSTATE); if ( NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(GETRSCSTATE); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETRSCSTATE, "ch_cmd"); return NULL; } ha_msg_del(msg); /* get the return msg */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETRSCSTATE); return NULL; } /* get the state of the resource from the message */ if (HA_OK != ha_msg_value_int(ret, F_LRM_STATE, &state)) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_STATE, ret); ha_msg_del(ret); return NULL; } *cur_state = (state_flag_t)state; /* the first msg includes the count of pending ops. */ if (HA_OK != ha_msg_value_int(ret, F_LRM_OPCNT, &op_count)) { LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPCNT, ret); ha_msg_del(ret); return NULL; } ha_msg_del(ret); for (i = 0; i < op_count; i++) { /* one msg for one op */ op_msg = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == op_msg) { cl_log(LOG_WARNING, "%s(%d): failed to receive a " "(pending operation) message from lrmd." , __FUNCTION__, __LINE__); continue; } op = msg_to_op(op_msg); /* add msg to the return list */ if (NULL != op) { op_list = g_list_append(op_list, op); } else { cl_log(LOG_WARNING, "%s(%d): failed to make a operation " "from a message with function msg_to_op" , __FUNCTION__, __LINE__); } ha_msg_del(op_msg); } op_list = g_list_sort(op_list, compare_call_id); /* Delete the duplicate op for call_id */ #if 0 cl_log(LOG_WARNING, "Before uniquing"); tmplist = g_list_first(op_list); while (tmplist != NULL) { cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id); tmplist = g_list_next(tmplist); } #endif tmplist = g_list_first(op_list); while (tmplist != NULL) { if (NULL != g_list_previous(tmplist)) { if (((lrm_op_t*)(g_list_previous(tmplist)->data))->call_id == ((lrm_op_t*)(tmplist->data))->call_id) { op_list = g_list_remove_link (op_list, tmplist); free_op((lrm_op_t *)tmplist->data); g_list_free_1(tmplist); tmplist = g_list_first(op_list); } } tmplist = g_list_next(tmplist); } #if 0 cl_log(LOG_WARNING, "After uniquing"); while (tmplist != NULL) { cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id); tmplist = g_list_next(tmplist); } #endif return op_list; } static lrm_op_t* rsc_get_last_result (lrm_rsc_t* rsc, const char* op_type) { struct ha_msg* msg = NULL; struct ha_msg* ret = NULL; lrm_op_t* op = NULL; int opcount = 0; /* check whether the channel to lrmd is available */ if (NULL == ch_cmd) { cl_log(LOG_ERR, "rsc_get_last_result: ch_mod is null."); return NULL; } /* check parameter */ if (NULL == rsc) { cl_log(LOG_ERR, "rsc_get_last_result: parameter rsc is null."); return NULL; } /* create the msg of get last op */ msg = create_lrm_rsc_msg(rsc->id,GETLASTOP); if (NULL == msg) { LOG_FAIL_create_lrm_rsc_msg(GETLASTOP); return NULL; } if (HA_OK != ha_msg_add(msg, F_LRM_RID, rsc->id)) { LOG_BASIC_ERROR("ha_msg_add"); ha_msg_del(msg); return NULL; } if (HA_OK != ha_msg_add(msg, F_LRM_OP, op_type)) { LOG_BASIC_ERROR("ha_msg_add"); ha_msg_del(msg); return NULL; } /* send the msg to lrmd */ if (HA_OK != msg2ipcchan(msg,ch_cmd)) { ha_msg_del(msg); LOG_FAIL_SEND_MSG(GETLASTOP, "ch_cmd"); return NULL; } /* get the return msg */ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR); if (NULL == ret) { LOG_FAIL_receive_reply(GETLASTOP); ha_msg_del(msg); return NULL; } if (HA_OK != ha_msg_value_int(ret,F_LRM_OPCNT, &opcount)) { op = NULL; } else if ( 1 == opcount ) { op = msg_to_op(ret); } ha_msg_del(msg); ha_msg_del(ret); return op; } /* * following are the implements of the utility functions */ lrm_op_t* lrm_op_new(void) { lrm_op_t* op; op = g_new0(lrm_op_t, 1); op->op_status = LRM_OP_PENDING; return op; } static lrm_op_t* msg_to_op(struct ha_msg* msg) { lrm_op_t* op; const char* op_type; const char* app_name; const char* rsc_id; const char* fail_reason; const char* output; const void* user_data; op = lrm_op_new(); /* op->timeout, op->interval, op->target_rc, op->call_id*/ if (HA_OK != ha_msg_value_int(msg,F_LRM_TIMEOUT, &op->timeout) || HA_OK != ha_msg_value_int(msg,F_LRM_INTERVAL, &op->interval) || HA_OK != ha_msg_value_int(msg,F_LRM_TARGETRC, &op->target_rc) || HA_OK != ha_msg_value_int(msg,F_LRM_DELAY, &op->start_delay) || HA_OK != ha_msg_value_int(msg,F_LRM_CALLID, &op->call_id)) { LOG_BASIC_ERROR("ha_msg_value_int"); free_op(op); return NULL; } /* op->op_status */ if (HA_OK != ha_msg_value_int(msg, F_LRM_OPSTATUS, (int*)&op->op_status)) { LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPSTATUS, msg); op->op_status = LRM_OP_PENDING; } /* if it finished successfully */ if (LRM_OP_DONE == op->op_status ) { /* op->rc */ if (HA_OK != ha_msg_value_int(msg, F_LRM_RC, &op->rc)) { free_op(op); LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RC, msg); return NULL; } /* op->output */ output = cl_get_string(msg, F_LRM_DATA); if (NULL != output){ op->output = g_strdup(output); } else { op->output = NULL; } } else if(op->op_status == LRM_OP_PENDING) { op->rc = EXECRA_STATUS_UNKNOWN; } else { op->rc = EXECRA_EXEC_UNKNOWN_ERROR; } /* op->app_name */ app_name = ha_msg_value(msg, F_LRM_APP); if (NULL == app_name) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_APP, msg); free_op(op); return NULL; } op->app_name = g_strdup(app_name); /* op->op_type */ op_type = ha_msg_value(msg, F_LRM_OP); if (NULL == op_type) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_OP, msg); free_op(op); return NULL; } op->op_type = g_strdup(op_type); /* op->rsc_id */ rsc_id = ha_msg_value(msg, F_LRM_RID); if (NULL == rsc_id) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RID, msg); free_op(op); return NULL; } op->rsc_id = g_strdup(rsc_id); /* op->fail_reason present only on async failures */ fail_reason = ha_msg_value(msg, F_LRM_FAIL_REASON); if (fail_reason) { op->fail_reason = g_strdup(fail_reason); } /* op->user_data */ user_data = cl_get_string(msg, F_LRM_USERDATA); if (NULL != user_data) { op->user_data = g_strdup(user_data); } /* time_stamps */ if (ha_msg_value_ul(msg, F_LRM_T_RUN, &op->t_run) != HA_OK || ha_msg_value_ul(msg, F_LRM_T_RCCHANGE, &op->t_rcchange) != HA_OK || ha_msg_value_ul(msg, F_LRM_EXEC_TIME, &op->exec_time) != HA_OK || ha_msg_value_ul(msg, F_LRM_QUEUE_TIME, &op->queue_time) != HA_OK) { /* cl_log(LOG_WARNING , "%s:%d: failed to get the timing information" , __FUNCTION__, __LINE__); */ } /* op->params */ op->params = ha_msg_value_str_table(msg, F_LRM_PARAM); ha_msg_value_int(msg, F_LRM_RSCDELETED, &op->rsc_deleted); return op; } static struct ha_msg* op_to_msg (lrm_op_t* op) { struct ha_msg* msg = ha_msg_new(15); if (!msg) { LOG_BASIC_ERROR("ha_msg_new"); return NULL; } if (HA_OK != ha_msg_add(msg, F_LRM_TYPE, PERFORMOP) || HA_OK != ha_msg_add(msg, F_LRM_RID, op->rsc_id) || HA_OK != ha_msg_add(msg, F_LRM_OP, op->op_type) || HA_OK != ha_msg_add_int(msg, F_LRM_TIMEOUT, op->timeout) || HA_OK != ha_msg_add_int(msg, F_LRM_INTERVAL, op->interval) || HA_OK != ha_msg_add_int(msg, F_LRM_DELAY, op->start_delay) || HA_OK != ha_msg_add_int(msg, F_LRM_COPYPARAMS, op->copyparams) || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RUN,op->t_run) || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RCCHANGE, op->t_rcchange) || HA_OK != ha_msg_add_ul(msg, F_LRM_EXEC_TIME, op->exec_time) || HA_OK != ha_msg_add_ul(msg, F_LRM_QUEUE_TIME, op->queue_time) || HA_OK != ha_msg_add_int(msg, F_LRM_TARGETRC, op->target_rc) || ( op->app_name && (HA_OK != ha_msg_add(msg, F_LRM_APP, op->app_name))) || ( op->user_data && (HA_OK != ha_msg_add(msg,F_LRM_USERDATA,op->user_data))) || ( op->params && (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,op->params)))) { LOG_BASIC_ERROR("op_to_msg conversion failed"); ha_msg_del(msg); return NULL; } return msg; } static int get_ret_from_ch(IPC_Channel* ch) { int ret; struct ha_msg* msg; msg = msgfromIPC(ch, MSG_ALLOWINTR); if (NULL == msg) { cl_log(LOG_ERR , "%s(%d): failed to receive message with function msgfromIPC" , __FUNCTION__, __LINE__); return HA_FAIL; } if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg); ha_msg_del(msg); return HA_FAIL; } ha_msg_del(msg); return ret; } static int get_ret_from_msg(struct ha_msg* msg) { int ret; if (NULL == msg) { cl_log(LOG_ERR, "%s(%d): the parameter is a NULL pointer." , __FUNCTION__, __LINE__); return HA_FAIL; } if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) { LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg); return HA_FAIL; } return ret; } static void free_op (lrm_op_t* op) { if (NULL == op) { return; } if (NULL != op->op_type) { g_free(op->op_type); } if (NULL != op->output) { g_free(op->output); } if (NULL != op->rsc_id) { g_free(op->rsc_id); } if (NULL != op->app_name) { g_free(op->app_name); } if (NULL != op->user_data) { g_free(op->user_data); } if (NULL != op->params) { free_str_table(op->params); } g_free(op); } void lrm_free_op(lrm_op_t* op) { free_op(op); } void lrm_free_rsc(lrm_rsc_t* rsc) { if (NULL == rsc) { return; } if (NULL != rsc->id) { g_free(rsc->id); } if (NULL != rsc->type) { g_free(rsc->type); } if (NULL != rsc->class) { g_free(rsc->class); } if (NULL != rsc->provider) { g_free(rsc->provider); } if (NULL != rsc->params) { free_str_table(rsc->params); } g_free(rsc); } void lrm_free_str_list(GList* list) { GList* item; if (NULL == list) { return; } item = g_list_first(list); while (NULL != item) { if (NULL != item->data) { g_free(item->data); } list = g_list_delete_link(list, item); item = g_list_first(list); } } void lrm_free_op_list(GList* list) { GList* item; if (NULL == list) { return; } item = g_list_first(list); while (NULL != item) { if (NULL != item->data) { free_op((lrm_op_t*)item->data); } list = g_list_delete_link(list, item); item = g_list_first(list); } } void lrm_free_str_table(GHashTable* table) { if (NULL != table) { free_str_table(table); } } const char * execra_code2string(uniform_ret_execra_t code) { switch(code) { case EXECRA_EXEC_UNKNOWN_ERROR: return "unknown exec error"; case EXECRA_NO_RA: return "no RA"; case EXECRA_OK: return "ok"; case EXECRA_UNKNOWN_ERROR: return "unknown error"; case EXECRA_INVALID_PARAM: return "invalid parameter"; case EXECRA_UNIMPLEMENT_FEATURE: return "unimplemented feature"; case EXECRA_INSUFFICIENT_PRIV: return "insufficient privileges"; case EXECRA_NOT_INSTALLED: return "not installed"; case EXECRA_NOT_CONFIGURED: return "not configured"; case EXECRA_NOT_RUNNING: return "not running"; /* For status command only */ case EXECRA_RUNNING_MASTER: return "master"; case EXECRA_FAILED_MASTER: return "master (failed)"; case EXECRA_RA_DEAMON_DEAD1: return "status: daemon dead"; case EXECRA_RA_DEAMON_DEAD2: return "status: daemon dead"; case EXECRA_RA_DEAMON_STOPPED: return "status: daemon stopped"; case EXECRA_STATUS_UNKNOWN: return "status: unknown"; default: break; } return ""; } Reusable-Cluster-Components-glue--3cff550e1084/lib/lrm/lrm_msg.c0000644000000000000000000001133012120057602024423 0ustar00usergroup00000000000000/* * Message Functions For Local Resource Manager * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * By Huang Zhen 2004/2/13 * */ #include #include #include #include #include #include #include #include #define LOG_BASIC_ERROR(apiname) \ cl_log(LOG_ERR, "%s(%d): %s failed.", __FUNCTION__, __LINE__, apiname) const lrm_op_t lrm_zero_op; /* Default initialized to zeros */ static void copy_pair(gpointer key, gpointer value, gpointer user_data) { GHashTable* taget_table = (GHashTable*)user_data; g_hash_table_insert(taget_table, g_strdup(key), g_strdup(value)); } GHashTable* copy_str_table(GHashTable* src_table) { GHashTable* target_table = NULL; if ( NULL == src_table) { return NULL; } target_table = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_foreach(src_table, copy_pair, target_table); return target_table; } static void merge_pair(gpointer key, gpointer value, gpointer user_data) { GHashTable *merged = (GHashTable*)user_data; if (g_hash_table_lookup(merged, key)) { return; } g_hash_table_insert(merged, g_strdup(key), g_strdup(value)); } GHashTable* merge_str_tables(GHashTable* old, GHashTable* new) { GHashTable* merged = NULL; if ( NULL == old ) { return copy_str_table(new); } if ( NULL == new ) { return copy_str_table(old); } merged = copy_str_table(new); g_hash_table_foreach(old, merge_pair, merged); return merged; } static gboolean free_pair(gpointer key, gpointer value, gpointer user_data) { g_free(key); g_free(value); return TRUE; } void free_str_table(GHashTable* hash_table) { g_hash_table_foreach_remove(hash_table, free_pair, NULL); g_hash_table_destroy(hash_table); } struct ha_msg* create_lrm_msg (const char* msg) { struct ha_msg* ret; if ((NULL == msg) || (0 == strlen(msg))) { return NULL; } ret = ha_msg_new(1); if (HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg)) { ha_msg_del(ret); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } return ret; } struct ha_msg* create_lrm_reg_msg(const char* app_name) { struct ha_msg* ret; if ((NULL == app_name) || (0 == strlen(app_name))) { return NULL; } ret = ha_msg_new(5); if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, REGISTER) || HA_OK != ha_msg_add(ret, F_LRM_APP, app_name) || HA_OK != ha_msg_add_int(ret, F_LRM_PID, getpid()) || HA_OK != ha_msg_add_int(ret, F_LRM_GID, getegid()) || HA_OK != ha_msg_add_int(ret, F_LRM_UID, getuid())) { ha_msg_del(ret); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } return ret; } struct ha_msg* create_lrm_addrsc_msg(const char* rid, const char* class, const char* type, const char* provider, GHashTable* params) { struct ha_msg* msg; if (NULL==rid||NULL==class||NULL==type) { return NULL; } msg = ha_msg_new(5); if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, ADDRSC) || HA_OK != ha_msg_add(msg, F_LRM_RID, rid) || HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class) || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } if( provider ) { if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } } if ( params ) { if (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,params)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } } return msg; } struct ha_msg* create_lrm_rsc_msg(const char* rid, const char* msg) { struct ha_msg* ret; if ((NULL == rid) ||(NULL == msg) || (0 == strlen(msg))) { return NULL; } ret = ha_msg_new(2); if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg) || HA_OK != ha_msg_add(ret, F_LRM_RID, rid)) { ha_msg_del(ret); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } return ret; } struct ha_msg* create_lrm_ret(int ret, int fields) { struct ha_msg* msg = ha_msg_new(fields); if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, RETURN) || HA_OK != ha_msg_add_int(msg, F_LRM_RET, ret)) { ha_msg_del(msg); LOG_BASIC_ERROR("ha_msg_add"); return NULL; } return msg; } Reusable-Cluster-Components-glue--3cff550e1084/lib/lrm/racommon.c0000644000000000000000000001040112120057602024574 0ustar00usergroup00000000000000/* * Common functions for LRM interface to resource agents * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: racommon.c * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * */ #include #include #include #include #include #include #include #include /* Add it for compiling on OSX */ #include #include #include #include #include void get_ra_pathname(const char* class_path, const char* type, const char* provider, char pathname[]) { char* type_dup; char* base_name; type_dup = g_strndup(type, RA_MAX_NAME_LENGTH); if (type_dup == NULL) { cl_log(LOG_ERR, "No enough memory to allocate."); pathname[0] = '\0'; return; } base_name = basename(type_dup); if ( strncmp(type, base_name, RA_MAX_NAME_LENGTH) == 0 ) { /*the type does not include path*/ if (provider) { snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s/%s", class_path, provider, type); }else{ snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s", class_path,type); } }else{ /*the type includes path, just copy it to pathname*/ if ( *type == '/' ) { g_strlcpy(pathname, type, RA_MAX_NAME_LENGTH); } else { *pathname = '\0'; cl_log(LOG_ERR, "%s: relative paths not allowed: %s", __FUNCTION__, type); } } g_free(type_dup); } /* * Description: Filter a file. * Return Value: * TRUE: the file is qualified. * FALSE: the file is unqualified. * Notes: A qualifed file is a regular file with execute bits * which does not start with '.' */ gboolean filtered(char * file_name) { struct stat buf; char *s; if ( stat(file_name, &buf) != 0 ) { return FALSE; } if ( ((s = strrchr(file_name,'/')) && *(s+1) == '.') || *file_name == '.' ) { return FALSE; } if ( S_ISREG(buf.st_mode) && ( ( buf.st_mode & S_IXUSR ) || ( buf.st_mode & S_IXGRP ) || ( buf.st_mode & S_IXOTH ) ) ) { return TRUE; } return FALSE; } int get_runnable_list(const char* class_path, GList ** rsc_info) { struct dirent **namelist; int file_num; if ( rsc_info == NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list"); return -2; } if ( *rsc_info != NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list."\ "will cause memory leak."); *rsc_info = NULL; } file_num = scandir(class_path, &namelist, NULL, alphasort); if (file_num < 0) { cl_log(LOG_ERR, "scandir failed in RA plugin"); return -2; } else{ while (file_num--) { char tmp_buffer[FILENAME_MAX+1]; tmp_buffer[0] = '\0'; tmp_buffer[FILENAME_MAX] = '\0'; snprintf(tmp_buffer, FILENAME_MAX, "%s/%s", class_path, namelist[file_num]->d_name ); if ( filtered(tmp_buffer) == TRUE ) { *rsc_info = g_list_append(*rsc_info, g_strdup(namelist[file_num]->d_name)); } free(namelist[file_num]); } free(namelist); } return g_list_length(*rsc_info); } int get_failed_exec_rc(void) { int rc; switch (errno) { /* see execve(2) */ case ENOENT: /* No such file or directory */ case EISDIR: /* Is a directory */ rc = EXECRA_NOT_INSTALLED; break; case EACCES: /* permission denied (various errors) */ rc = EXECRA_INSUFFICIENT_PRIV; break; default: rc = EXECRA_EXEC_UNKNOWN_ERROR; break; } return rc; } void closefiles(void) { int fd; /* close all descriptors except stdin/out/err and channels to logd */ for (fd = getdtablesize() - 1; fd > STDERR_FILENO; fd--) { /*if (!cl_log_is_logd_fd(fd))*/ close(fd); } } Reusable-Cluster-Components-glue--3cff550e1084/lib/pils/Makefile.am0000644000000000000000000000332612120057602025036 0ustar00usergroup00000000000000# # pils: Linux-HA heartbeat code # # Copyright (C) 2001 Alan Robertson # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl AM_CFLAGS = @CFLAGS@ ## include files #pkginclude_HEADERS = $(top_srcdir)/include/pils/plugin.h \ # $(top_srcdir)/include/pils/interface.h ## binaries #sbin_PROGRAMS = main #main_SOURCES = main.c #main_LDADD = libpils.la @LIBLTDL@ \ # $(GLIBLIB) \ # $(top_builddir)/replace/libreplace.la #main_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ ## libraries lib_LTLIBRARIES = libpils.la plugindir = $(libdir)/@HB_PKG@/plugins/test plugin_LTLIBRARIES = test.la libpils_la_SOURCES = pils.c libpils_la_LDFLAGS = -version-info 2:0:0 libpils_la_LIBADD = $(top_builddir)/replace/libreplace.la \ @LIBLTDL@ $(GLIBLIB) test_la_SOURCES = test.c test_la_LDFLAGS = -export-dynamic -module -avoid-version Reusable-Cluster-Components-glue--3cff550e1084/lib/pils/main.c0000644000000000000000000000555212120057602024075 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #define MOD "/home/alanr/modules" GHashTable* test1functions = NULL; long one = 1; long two = 2; long three = 3; long four = 4; static int TestCallBack ( GenericPILCallbackType t , PILPluginUniv* univ , const char * iftype , const char * ifname , void* userptr ); static PILGenericIfMgmtRqst RegRqsts [] = { {"test", &test1functions, &one, TestCallBack, &two}, {NULL, NULL, NULL, NULL, NULL} }; int main(int argc, char ** argv) { PILPluginUniv * u; PIL_rc rc; int j; u = NewPILPluginUniv(MOD); /* PILSetDebugLevel(u, NULL, NULL, 0); */ PILLogMemStats(); if ((rc = PILLoadPlugin(u, "InterfaceMgr", "generic", &RegRqsts)) != PIL_OK) { fprintf(stderr, "generic plugin load Error = [%s]\n" , lt_dlerror()); /*exit(1);*/ } /* PILSetDebugLevel(u, NULL, NULL, 0); */ for (j=0; j < 10; ++j) { PILLogMemStats(); fprintf(stderr, "****Loading plugin test/test\n"); if ((rc = PILLoadPlugin(u, "test", "test", NULL)) != PIL_OK) { printf("ERROR: test plugin load error = [%d/%s]\n" , rc, lt_dlerror()); } PILLogMemStats(); fprintf(stderr, "****UN-loading plugin test/test\n"); if ((rc = PILIncrIFRefCount(u, "test", "test", -1))!= PIL_OK){ printf("ERROR: test plugin UNload error = [%d/%s]\n" , rc, lt_dlerror()); } } PILLogMemStats(); DelPILPluginUniv(u); u = NULL; PILLogMemStats(); return 0; } static int TestCallBack ( GenericPILCallbackType t , PILPluginUniv* univ , const char * iftype , const char * ifname , void* userptr) { char cbbuf[32]; switch(t) { case PIL_REGISTER: snprintf(cbbuf, sizeof(cbbuf), "PIL_REGISTER"); break; case PIL_UNREGISTER: snprintf(cbbuf, sizeof(cbbuf), "PIL_UNREGISTER"); break; default: snprintf(cbbuf, sizeof(cbbuf), "type [%d?]", t); break; } fprintf(stderr, "Callback: (%s, univ: 0x%lx, module: %s/%s, user ptr: 0x%lx (%ld))\n" , cbbuf , (unsigned long) univ , iftype, ifname , (unsigned long)userptr , (*((long *)userptr))); return PIL_OK; } Reusable-Cluster-Components-glue--3cff550e1084/lib/pils/pils.c0000644000000000000000000015356412120057602024127 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include /* Dumbness... */ #define time FooTimeParameter #define index FooIndexParameter # include #undef time #undef index #define ENABLE_PIL_DEFS_PRIVATE #define ENABLE_PLUGIN_MANAGER_PRIVATE #ifndef STRLEN_CONST # define STRLEN_CONST(con) (sizeof(con)-1) #endif #include #define NEW(type) (g_new(type,1)) #define ZAP(obj) memset(obj, 0, sizeof(*obj)) #define DELETE(obj) {g_free(obj); obj = NULL;} #ifdef LTDL_SHLIB_EXT # define PLUGINSUFFIX LTDL_SHLIB_EXT #else # define PLUGINSUFFIX ".so" #endif static int PluginDebugLevel = 0; #define DEBUGPLUGIN (PluginDebugLevel > 0) static PIL_rc InterfaceManager_plugin_init(PILPluginUniv* univ); static char** PILPluginTypeListPlugins(PILPluginType* pitype, int* picount); static PILInterface* FindIF(PILPluginUniv* universe, const char *iftype , const char * ifname); static PIL_rc PluginExists(const char * PluginPath); static char * PILPluginPath(PILPluginUniv* universe, const char * plugintype , const char * pluginname); void DelPILPluginUniv(PILPluginUniv*); /* * These RmA* functions primarily called from hash_table_foreach, * functions, so they have gpointer arguments. When calling * them by hand, take special care to pass the right argument types. * * They all follow the same calling sequence though. It is: * String name"*" type object * "*" type object with the name given by 1st argument * NULL * * For example: * RmAPILPluginType takes * string name * PILPluginType* object with the given name. */ static gboolean RmAPILPluginType ( gpointer pitname /* Name of this plugin type */ , gpointer pitype /* PILPluginType* */ , gpointer notused ); static void RemoveAPILPluginType(PILPluginType*); static PILPluginType* NewPILPluginType ( PILPluginUniv* pluginuniv , const char * plugintype ); static void DelPILPluginType(PILPluginType*); /* * These RmA* functions primarily called from hash_table_foreach, * so they have gpointer arguments. When calling * them by hand, take special care to pass the right argument types. */ static gboolean RmAPILPlugin ( gpointer piname /* Name of this plugin */ , gpointer plugin /* PILPlugin* */ , gpointer notused ); static void RemoveAPILPlugin(PILPlugin*); static PILPlugin* NewPILPlugin(PILPluginType* pitype , const char * plugin_name , lt_dlhandle dlhand , PILPluginInitFun PluginSym); static void DelPILPlugin(PILPlugin*); struct MemStat { unsigned long news; unsigned long frees; }; static struct PluginStats { struct MemStat plugin; struct MemStat pitype; struct MemStat piuniv; struct MemStat interface; struct MemStat interfacetype; struct MemStat interfaceuniv; }PILstats; #define STATNEW(t) {PILstats.t.news ++; } #define STATFREE(t) {PILstats.t.frees ++; } static PILInterfaceUniv* NewPILInterfaceUniv(PILPluginUniv*); static void DelPILInterfaceUniv(PILInterfaceUniv*); /* * These RmA* functions primarily called from hash_table_foreach, but * not necessarily, so they have gpointer arguments. When calling * them by hand, take special care to pass the right argument types. */ static gboolean RmAPILInterfaceType ( gpointer iftypename /* Name of this interface type */ , gpointer iftype /* PILInterfaceType* */ , gpointer notused ); static void RemoveAPILInterfaceType(PILInterfaceType*, PILInterfaceType*); static PILInterfaceType* NewPILInterfaceType ( PILInterfaceUniv* , const char * typename , void* ifexports, void* user_data ); static void DelPILInterfaceType(PILInterfaceType*); /* * These RmA* functions are designed to be called from * hash_table_foreach, so they have gpointer arguments. When calling * them by hand, take special care to pass the right argument types. * They can be called from other places safely also. */ static gboolean RmAPILInterface ( gpointer ifname /* Name of this interface */ , gpointer plugin /* PILInterface* */ , gpointer notused ); static PIL_rc RemoveAPILInterface(PILInterface*); static void DelPILPluginType(PILPluginType*); static PILInterface* NewPILInterface ( PILInterfaceType* interfacetype , const char* interfacename , void * exports , PILInterfaceFun closefun , void* ud_interface , PILPlugin* loading_plugin /* The plugin that loaded us */ ); static void DelPILInterface(PILInterface*); static PIL_rc close_ifmgr_interface(PILInterface*, void*); /* * For consistency, we show up as a plugin in our our system. * * Here are our exports as a plugin. * */ static const char * PIL_PILPluginVersion(void); static void PIL_PILPluginClose (PILPlugin*); void PILpisysSetDebugLevel (int level); int PILpisysGetDebugLevel(void); static const char * PIL_PILPluginLicense (void); static const char * PIL_PILPluginLicenseUrl (void); static const PILPluginOps PluginExports = { PIL_PILPluginVersion , PILpisysGetDebugLevel , PILpisysSetDebugLevel , PIL_PILPluginLicense , PIL_PILPluginLicenseUrl , PIL_PILPluginClose }; /* Prototypes for the functions that we export to every plugin */ static PIL_rc PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* mops); static PIL_rc PILunregister_plugin(PILPlugin* piinfo); static PIL_rc PILRegisterInterface ( PILPlugin* piinfo , const char * interfacetype /* Type of interface */ , const char * interfacename /* Name of interface */ , void* Ops /* Ops exported by this interface */ , PILInterfaceFun closefunc /* Ops exported by this interface */ , PILInterface** interfaceid /* Interface id (OP) */ , void** Imports /* Functions imported by */ /* this interface (OP) */ , void* ud_interface /* interface user data */ ); static PIL_rc PILunregister_interface(PILInterface* interfaceid); static void PILLog(PILLogLevel priority, const char * fmt, ...); /* * This is the set of functions that we export to every plugin * * That also makes it the set of functions that every plugin imports. * */ static PILPluginImports PILPluginImportSet = { PILregister_plugin /* register_plugin */ , PILunregister_plugin /* unregister_plugin */ , PILRegisterInterface /* register_interface */ , RemoveAPILInterface /* unregister_interface */ , PILLoadPlugin /* load_plugin */ , PILLog /* Logging function */ , g_malloc /* Malloc function */ , g_realloc /* realloc function */ , g_free /* Free function */ , g_strdup /* Strdup function */ }; static PIL_rc ifmgr_register_interface(PILInterface* newif , void** imports); static PIL_rc ifmgr_unregister_interface(PILInterface* interface); /* * For consistency, the master interface manager is a interface in the * system. Below is our set of exported Interface functions. * * Food for thought: This is the interface manager whose name is * interface. This makes it the Interface Interface interface ;-) * (or the Interface/Interface interface if you prefer) */ static PILInterfaceOps IfExports = { ifmgr_register_interface , ifmgr_unregister_interface }; /* * Below is the set of functions we export to every interface manager. */ static int IfRefCount(PILInterface * ifh); static int IfIncrRefCount(PILInterface*eifinfo,int plusminus); static int PluginIncrRefCount(PILPlugin*eifinfo,int plusminus); #if 0 static int PluginRefCount(PILPlugin * ifh); #endif static void IfForceUnregister(PILInterface *eifinfo); static void IfForEachClientRemove(PILInterface* manangerif , gboolean(*f)(PILInterface* clientif, void * other) , void* other); static PILInterfaceImports IFManagerImports = { IfRefCount , IfIncrRefCount , IfForceUnregister , IfForEachClientRemove }; static void PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype); static void PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv); static void PILValidatePluginUniv(gpointer key, gpointer pitype, gpointer); static void PILValidateInterface(gpointer key, gpointer interface, gpointer iftype); static void PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv); static void PILValidateInterfaceUniv(gpointer key, gpointer puniv, gpointer); /***************************************************************************** * * This code is for managing plugins, and interacting with them... * ****************************************************************************/ static PILPlugin* NewPILPlugin( PILPluginType* pitype , const char * plugin_name , lt_dlhandle dlhand , PILPluginInitFun PluginSym) { PILPlugin* ret = NEW(PILPlugin); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILPlugin(0x%x)", (unsigned long)ret); } STATNEW(plugin); ret->MagicNum = PIL_MAGIC_PLUGIN; ret->plugin_name = g_strdup(plugin_name); ret->plugintype = pitype; ret->refcnt = 0; ret->dlhandle = dlhand; ret->dlinitfun = PluginSym; PILValidatePlugin(ret->plugin_name, ret, pitype); return ret; } static void DelPILPlugin(PILPlugin*pi) { if (pi->refcnt > 0) { PILLog(PIL_INFO, "DelPILPlugin: Non-zero refcnt"); } if (pi->dlhandle) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Closing dlhandle for (%s/%s)" , pi->plugintype->plugintype, pi->plugin_name); } lt_dlclose(pi->dlhandle); }else if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NO dlhandle for (%s/%s)!" , pi->plugintype->plugintype, pi->plugin_name); } DELETE(pi->plugin_name); ZAP(pi); DELETE(pi); STATFREE(plugin); } static PILPluginType dummymlpitype = { PIL_MAGIC_PLUGINTYPE , NULL /*plugintype*/ , NULL /*piuniv*/ , NULL /*Plugins*/ , PILPluginTypeListPlugins /* listplugins */ }; static PILPluginType* NewPILPluginType(PILPluginUniv* pluginuniv , const char * plugintype ) { PILPluginType* ret = NEW(PILPluginType); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILPlugintype(0x%x)", (unsigned long)ret); } STATNEW(pitype); *ret = dummymlpitype; ret->plugintype = g_strdup(plugintype); ret->piuniv = pluginuniv; ret->Plugins = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(pluginuniv->PluginTypes , g_strdup(ret->plugintype), ret); PILValidatePluginType(ret->plugintype, ret, pluginuniv); return ret; } static void DelPILPluginType(PILPluginType*pitype) { PILValidatePluginType(NULL, pitype, NULL); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILPluginType(%s)", pitype->plugintype); } STATFREE(pitype); g_hash_table_foreach_remove(pitype->Plugins, RmAPILPlugin, NULL); g_hash_table_destroy(pitype->Plugins); DELETE(pitype->plugintype); ZAP(pitype); DELETE(pitype); } /* * These RmA* functions primarily called from hash_table_foreach, * so they have gpointer arguments. This *not necessarily* clause * is why they do the g_hash_table_lookup_extended call instead of * just deleting the key. When called from outside, the key * * may not be pointing at the key to actually free, but a copy * of the key. */ static gboolean RmAPILPlugin /* IsA GHFunc: required for g_hash_table_foreach_remove() */ ( gpointer piname /* Name of this plugin */ , gpointer plugin /* PILPlugin* */ , gpointer notused ) { PILPlugin* Plugin = plugin; PILPluginType* Pitype = Plugin->plugintype; PILValidatePlugin(piname, plugin, NULL); PILValidatePluginType(NULL, Pitype, NULL); g_assert(IS_PILPLUGIN(Plugin)); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILPlugin(%s/%s)", Pitype->plugintype , Plugin->plugin_name); } /* Normally called from g_hash_table_foreachremove or equivalent */ DelPILPlugin(plugin); DELETE(piname); return TRUE; } static void RemoveAPILPlugin(PILPlugin*Plugin) { PILPluginType* Pitype = Plugin->plugintype; gpointer key; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RemoveAPILPlugin(%s/%s)" , Pitype->plugintype , Plugin->plugin_name); } if (g_hash_table_lookup_extended(Pitype->Plugins , Plugin->plugin_name, &key, (void*)&Plugin)) { g_hash_table_remove(Pitype->Plugins, key); RmAPILPlugin(key, Plugin, NULL); key = NULL; Plugin = NULL; }else{ g_assert_not_reached(); } if (g_hash_table_size(Pitype->Plugins) == 0) { RemoveAPILPluginType(Pitype); /* Pitype is now invalid */ Pitype = NULL; } } PILPluginUniv* NewPILPluginUniv(const char * basepluginpath) { PILPluginUniv* ret = NEW(PILPluginUniv); /* The delimiter separating search path components */ const char* path_delim = G_SEARCHPATH_SEPARATOR_S; char * fullpath; STATNEW(piuniv); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILPluginUniv(0x%x)" , (unsigned long)ret); } if (!g_path_is_absolute(basepluginpath)) { DELETE(ret); return(ret); } ret->MagicNum = PIL_MAGIC_PLUGINUNIV; fullpath = g_strdup_printf("%s%s%s", basepluginpath , path_delim, PILS_BASE_PLUGINDIR); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "PILS: Plugin path = %s", fullpath); } /* Separate the root directory PATH into components */ ret->rootdirlist = g_strsplit(fullpath, path_delim, 100); g_free(fullpath); ret->PluginTypes = g_hash_table_new(g_str_hash, g_str_equal); ret->imports = &PILPluginImportSet; ret->ifuniv = NewPILInterfaceUniv(ret); PILValidatePluginUniv(NULL, ret, NULL); return ret; } /* Change memory allocation functions immediately after creating universe */ void PilPluginUnivSetMemalloc(PILPluginUniv* u , gpointer (*allocfun)(glib_size_t size) , gpointer (*reallocfun)(gpointer ptr, glib_size_t size) , void (*freefun)(void* space) , char* (*strdupfun)(const char *s)) { u->imports->alloc = allocfun; u->imports->mrealloc = reallocfun; u->imports->mfree = freefun; u->imports->mstrdup = strdupfun; } /* Change logging functions - preferably right after creating universe */ void PilPluginUnivSetLog(PILPluginUniv* u , void (*logfun) (PILLogLevel priority, const char * fmt, ...)) { u->imports->log = logfun; } void DelPILPluginUniv(PILPluginUniv* piuniv) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILPluginUniv(0x%lx)" , (unsigned long)piuniv); } STATFREE(piuniv); PILValidatePluginUniv(NULL, piuniv, NULL); DelPILInterfaceUniv(piuniv->ifuniv); piuniv->ifuniv = NULL; g_hash_table_foreach_remove(piuniv->PluginTypes , RmAPILPluginType, NULL); g_hash_table_destroy(piuniv->PluginTypes); g_strfreev(piuniv->rootdirlist); ZAP(piuniv); DELETE(piuniv); } /* * These RmA* functions primarily called from hash_table_foreach, * so they have gpointer arguments. This *not necessarily* clause * is why they do the g_hash_table_lookup_extended call instead of * just deleting the key. When called from outside, the key * * may not be pointing at the key to actually free, but a copy * of the key. */ static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */ RmAPILPluginType ( gpointer pitname /* Name of this plugin type "real" key */ , gpointer pitype /* PILPluginType* */ , gpointer notused ) { PILPluginType* Plugintype = pitype; g_assert(IS_PILPLUGINTYPE(Plugintype)); PILValidatePluginType(pitname, pitype, NULL); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILPluginType(%s)" , Plugintype->plugintype); } /* * This function is usually but not always called by * g_hash_table_foreach_remove() */ DelPILPluginType(pitype); DELETE(pitname); return TRUE; } static void RemoveAPILPluginType(PILPluginType*Plugintype) { PILPluginUniv* Pluginuniv = Plugintype->piuniv; gpointer key; if (g_hash_table_lookup_extended(Pluginuniv->PluginTypes , Plugintype->plugintype, &key, (void*)&Plugintype)) { g_hash_table_remove(Pluginuniv->PluginTypes, key); RmAPILPluginType(key, Plugintype, NULL); }else{ g_assert_not_reached(); } } /* * InterfaceManager_plugin_init: Initialize the handling of * "Interface Manager" interfaces. * * There are a few potential bootstrapping problems here ;-) * */ static PIL_rc InterfaceManager_plugin_init(PILPluginUniv* univ) { PILPluginImports* imports = univ->imports; PILPluginType* pitype; PILInterface* ifinfo; PILInterfaceType* iftype; void* dontcare; PILPlugin* ifmgr_plugin; PIL_rc rc; iftype = NewPILInterfaceType(univ->ifuniv, PI_IFMANAGER, &IfExports , NULL); g_hash_table_insert(univ->ifuniv->iftypes , g_strdup(PI_IFMANAGER), iftype); pitype = NewPILPluginType(univ, PI_IFMANAGER); g_hash_table_insert(univ->PluginTypes , g_strdup(PI_IFMANAGER), pitype); ifmgr_plugin= NewPILPlugin(pitype, PI_IFMANAGER, NULL, NULL); g_hash_table_insert(pitype->Plugins , g_strdup(PI_IFMANAGER), ifmgr_plugin); /* We can call register_plugin, since it doesn't depend on us... */ rc = imports->register_plugin(ifmgr_plugin, &PluginExports); if (rc != PIL_OK) { PILLog(PIL_CRIT, "register_plugin() failed in init: %s" , PIL_strerror(rc)); return(rc); } /* * Now, we're registering interfaces, and are into some deep * Catch-22 if do it the "easy" way, since our code is * needed in order to support interface loading for the type of * interface we are (a Interface interface). * * So, instead of calling imports->register_interface(), we have to * do the work ourselves here... * * Since no one should yet be registered to handle Interface * interfaces, we need to bypass the hash table handler lookup * that register_interface would do and call the function that * register_interface would call... * */ /* The first argument is the PILInterfaceType* */ ifinfo = NewPILInterface(iftype, PI_IFMANAGER, &IfExports , close_ifmgr_interface, NULL, NULL); ifinfo->ifmanager = iftype->ifmgr_ref = ifinfo; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "InterfaceManager_plugin_init(0x%lx/%s)" , (unsigned long)ifinfo, ifinfo->interfacename); } PILValidatePluginUniv(NULL, univ, NULL); ifmgr_register_interface(ifinfo, &dontcare); PILValidatePluginUniv(NULL, univ, NULL); return(PIL_OK); }/*InterfaceManager_plugin_init*/ /* Return current IfIf "plugin" version (not very interesting for us) */ static const char * PIL_PILPluginVersion(void) { return("1.0"); } /* Return current IfIf debug level */ int PILpisysGetDebugLevel(void) { return(PluginDebugLevel); } /* Set current IfIf debug level */ void PILpisysSetDebugLevel (int level) { PluginDebugLevel = level; } struct set_debug_helper { const char * pitype; const char * piname; int level; }; static void PILSetDebugLeveltoPlugin(gpointer key, gpointer plugin, gpointer Helper) { PILPlugin* p = plugin; struct set_debug_helper* helper = Helper; p->pluginops->setdebuglevel(helper->level); } static void PILSetDebugLevelbyType(const void * key, gpointer plugintype, gpointer Helper) { struct set_debug_helper* helper = Helper; PILPluginType* t = plugintype; if (helper->piname == NULL) { g_hash_table_foreach(t->Plugins, PILSetDebugLeveltoPlugin , helper); }else{ PILPlugin* p = g_hash_table_lookup(t->Plugins , helper->piname); if (p != NULL) { p->pluginops->setdebuglevel(helper->level); } } } void PILSetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname , int level) { struct set_debug_helper helper = {pitype, piname, level}; if (u == NULL) { return; } if (pitype == NULL) { g_hash_table_foreach(u->PluginTypes /* * Reason for this next cast: * SetDebugLevelbyType takes const gpointer * arguments, unlike a GHFunc which doesn't. */ , (GHFunc)PILSetDebugLevelbyType , &helper); }else{ PILPluginType* t = g_hash_table_lookup(u->PluginTypes , pitype); if (t != NULL) { PILSetDebugLevelbyType(pitype, t, &helper); } } } int PILGetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname) { PILPluginType* t; PILPlugin* p; if ( u == NULL || pitype == NULL || (t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL || (p = g_hash_table_lookup(t->Plugins, piname)) == NULL) { return -1; } return p->pluginops->getdebuglevel(); } /* Close/shutdown our PILPlugin (the interface manager interface plugin) */ /* All our interfaces will have already been shut down and unregistered */ static void PIL_PILPluginClose (PILPlugin* plugin) { } static const char * PIL_PILPluginLicense (void) { return LICENSE_LGPL; } static const char * PIL_PILPluginLicenseUrl (void) { return URL_LGPL; } /***************************************************************************** * * This code is for managing interfaces, and interacting with them... * ****************************************************************************/ static PILInterface* NewPILInterface(PILInterfaceType* interfacetype , const char* interfacename , void * exports , PILInterfaceFun closefun , void* ud_interface , PILPlugin* loading_plugin) { PILInterface* ret = NULL; PILInterface* look = NULL; if ((look = g_hash_table_lookup(interfacetype->interfaces , interfacename)) != NULL) { PILLog(PIL_DEBUG, "Deleting PILInterface!"); DelPILInterface(look); } ret = NEW(PILInterface); STATNEW(interface); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILInterface(0x%x)", (unsigned long)ret); } if (ret) { ret->MagicNum = PIL_MAGIC_INTERFACE; ret->interfacetype = interfacetype; ret->exports = exports; ret->ud_interface = ud_interface; ret->interfacename = g_strdup(interfacename); ret->ifmanager = interfacetype->ifmgr_ref; ret->loadingpi = loading_plugin; g_hash_table_insert(interfacetype->interfaces , g_strdup(ret->interfacename), ret); ret->if_close = closefun; ret->refcnt = 1; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILInterface(0x%lx:%s/%s)*** user_data: 0x%lx *******" , (unsigned long)ret , interfacetype->typename , ret->interfacename , ud_interface); } } return ret; } static void DelPILInterface(PILInterface* intf) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILInterface(0x%lx/%s)" , (unsigned long)intf, intf->interfacename); } STATFREE(interface); DELETE(intf->interfacename); ZAP(intf); DELETE(intf); } static PILInterfaceType* NewPILInterfaceType(PILInterfaceUniv*univ, const char * typename , void* ifeports, void* user_data) { PILInterfaceType* ifmgr_types; PILInterface* ifmgr_ref; PILInterfaceType* ret = NEW(PILInterfaceType); STATNEW(interfacetype); ret->MagicNum = PIL_MAGIC_INTERFACETYPE; ret->typename = g_strdup(typename); ret->interfaces = g_hash_table_new(g_str_hash, g_str_equal); ret->ud_if_type = user_data; ret->universe = univ; ret->ifmgr_ref = NULL; /* Now find the pointer to our if type in the Interface Universe */ if ((ifmgr_types = g_hash_table_lookup(univ->iftypes, PI_IFMANAGER)) != NULL) { if ((ifmgr_ref=g_hash_table_lookup(ifmgr_types->interfaces , typename)) != NULL) { ret->ifmgr_ref = ifmgr_ref; }else { g_assert(strcmp(typename, PI_IFMANAGER) == 0); } }else { g_assert(strcmp(typename, PI_IFMANAGER) == 0); } /* Insert ourselves into our parent's table */ g_hash_table_insert(univ->iftypes, g_strdup(typename), ret); return ret; } static void DelPILInterfaceType(PILInterfaceType*ift) { PILInterfaceUniv* u = ift->universe; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILInterfaceType(%s)" , ift->typename); } STATFREE(interfacetype); PILValidateInterfaceUniv(NULL, u, NULL); /* * RmAPILInterface refuses to remove the interface for the * Interface manager, because it must be removed last. * * Otherwise we won't be able to unregister interfaces * for other types of objects, and we'll be very confused. */ g_hash_table_foreach_remove(ift->interfaces, RmAPILInterface, NULL); PILValidateInterfaceUniv(NULL, u, NULL); if (g_hash_table_size(ift->interfaces) > 0) { gpointer key, iftype; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "DelPILInterfaceType(%s): table size (%d)" , ift->typename, g_hash_table_size(ift->interfaces)); } if (g_hash_table_lookup_extended(ift->interfaces , PI_IFMANAGER, &key, &iftype)) { DelPILInterface((PILInterface*)iftype); DELETE(key); } } DELETE(ift->typename); g_hash_table_destroy(ift->interfaces); ZAP(ift); DELETE(ift); } /* * These RmA* functions primarily called from hash_table_foreach, * so they have gpointer arguments. This *not necessarily* clause * is why they do the g_hash_table_lookup_extended call instead of * just deleting the key. When called from outside, the key * * may not be pointing at the key to actually free, but a copy * of the key. */ static gboolean /* IsAGHFunc: required for g_hash_table_foreach_remove() */ RmAPILInterface ( gpointer ifname /* Name of this interface */ , gpointer intf /* PILInterface* */ , gpointer notused ) { PILInterface* If = intf; PILInterfaceType* Iftype = If->interfacetype; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILInterface(0x%lx/%s)" , (unsigned long)If, If->interfacename); } g_assert(IS_PILINTERFACE(If)); /* * Don't remove the master interface manager this way, or * Somebody will have a cow... */ if (If == If->ifmanager) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILInterface: skipping (%s)" , If->interfacename); } return FALSE; } PILValidateInterface(ifname, If, Iftype); PILValidateInterfaceType(NULL, Iftype, NULL); /* * This function is usually but not always called by * g_hash_table_foreach_remove() */ PILunregister_interface(If); PILValidateInterface(ifname, If, Iftype); PILValidateInterfaceType(NULL, Iftype, NULL); DELETE(ifname); DelPILInterface(If); return TRUE; } static PIL_rc RemoveAPILInterface(PILInterface* pif) { PILInterfaceType* Iftype = pif->interfacetype; gpointer key; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RemoveAPILInterface(0x%lx/%s)" , (unsigned long)pif, pif->interfacename); } if (g_hash_table_lookup_extended(Iftype->interfaces , pif->interfacename, &key, (void*)&pif)) { g_assert(IS_PILINTERFACE(pif)); g_hash_table_remove(Iftype->interfaces, key); RmAPILInterface(key, pif, NULL); }else{ g_assert_not_reached(); } if (g_hash_table_size(Iftype->interfaces) == 0) { /* The generic plugin handler doesn't want us to * delete it's types... */ if (Iftype->ifmgr_ref->refcnt <= 1) { RemoveAPILInterfaceType(Iftype, NULL); } } return PIL_OK; } /* Register a Interface Interface (Interface manager) */ static PIL_rc ifmgr_register_interface(PILInterface* intf , void** imports) { PILInterfaceType* ift = intf->interfacetype; PILInterfaceUniv* ifuniv = ift->universe; PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */ if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "Registering Implementation manager for" " Interface type '%s'" , intf->interfacename); } ifops = intf->exports; if (ifops->RegisterInterface == NULL || ifops->UnRegisterInterface == NULL) { PILLog(PIL_DEBUG, "ifmgr_register_interface(%s)" ": NULL exported function pointer" , intf->interfacename); return PIL_INVAL; } *imports = &IFManagerImports; if(g_hash_table_lookup(ifuniv->iftypes, intf->interfacename) == NULL){ /* It registers itself into ifuniv automatically */ NewPILInterfaceType(ifuniv,intf->interfacename, &IfExports , NULL); } return PIL_OK; } static gboolean RemoveAllClients(PILInterface*interface, void * managerif) { /* * Careful! We can't remove ourselves this way... * This gets taken care of as a special case in DelPILInterfaceUniv... */ if (managerif == interface) { return FALSE; } PILunregister_interface(interface); return TRUE; } /* Unconditionally unregister a interface manager (InterfaceMgr Interface) */ static PIL_rc ifmgr_unregister_interface(PILInterface* interface) { /* * We need to unregister every interface we manage */ if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "ifmgr_unregister_interface(%s)" , interface->interfacename); } IfForEachClientRemove(interface, RemoveAllClients, interface); return PIL_OK; } /* Called to close the Interface manager for type Interface */ static PIL_rc close_ifmgr_interface(PILInterface* us, void* ud_interface) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "close_ifmgr_interface(%s)" , us->interfacename); } /* Nothing much to do */ return PIL_OK; } /* Return the reference count for this interface */ static int IfRefCount(PILInterface * eifinfo) { return eifinfo->refcnt; } /* Modify the reference count for this interface */ static int IfIncrRefCount(PILInterface*eifinfo, int plusminus) { if(DEBUGPLUGIN) { PILLog(PIL_DEBUG, "IfIncrRefCount(%d + %d )" , eifinfo->refcnt, plusminus); } eifinfo->refcnt += plusminus; if (eifinfo->refcnt <= 0) { eifinfo->refcnt = 0; /* Unregister this interface. */ RemoveAPILInterface(eifinfo); return 0; } return eifinfo->refcnt; } #if 0 static int PluginRefCount(PILPlugin * pi) { return pi->refcnt; } #endif static int PluginIncrRefCount(PILPlugin*pi, int plusminus) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PluginIncrRefCount(%d + %d )" , pi->refcnt, plusminus); } pi->refcnt += plusminus; if (pi->refcnt <= 0) { pi->refcnt = 0; RemoveAPILPlugin(pi); return 0; } return pi->refcnt; } static PILInterface* FindIF(PILPluginUniv* universe, const char *iftype, const char * ifname) { PILInterfaceUniv* puniv; PILInterfaceType* ptype; if (universe == NULL || (puniv = universe->ifuniv) == NULL || (ptype=g_hash_table_lookup(puniv->iftypes, iftype))==NULL){ return NULL; } return g_hash_table_lookup(ptype->interfaces, ifname); } PIL_rc PILIncrIFRefCount(PILPluginUniv* mu , const char * interfacetype , const char * interfacename , int plusminus) { PILInterface* intf = FindIF(mu, interfacetype, interfacename); if (intf) { g_assert(IS_PILINTERFACE(intf)); IfIncrRefCount(intf, plusminus); return PIL_OK; } return PIL_NOPLUGIN; } int PILGetIFRefCount(PILPluginUniv* mu , const char * interfacetype , const char * interfacename) { PILInterface* intf = FindIF(mu, interfacetype, interfacename); return IfRefCount(intf); } static void IfForceUnregister(PILInterface *id) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "IfForceUnRegister(%s)" , id->interfacename); } RemoveAPILInterface(id); } struct f_e_c_helper { gboolean(*fun)(PILInterface* clientif, void * passalong); void* passalong; }; static gboolean IfForEachClientHelper(gpointer key , gpointer iftype, gpointer helper_v); static gboolean IfForEachClientHelper(gpointer unused, gpointer iftype, gpointer v) { struct f_e_c_helper* s = (struct f_e_c_helper*)v; g_assert(IS_PILINTERFACE((PILInterface*)iftype)); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "IfForEachClientHelper(%s)" , ((PILInterface*)iftype)->interfacename); } return s->fun((PILInterface*)iftype, s->passalong); } static void IfForEachClientRemove ( PILInterface* mgrif , gboolean(*f)(PILInterface* clientif, void * passalong) , void* passalong /* usually PILInterface* */ ) { PILInterfaceType* mgrt; PILInterfaceUniv* u; const char * ifname; PILInterfaceType* clientt; struct f_e_c_helper h = {f, passalong}; if (mgrif == NULL || (mgrt = mgrif->interfacetype) == NULL || (u = mgrt->universe) == NULL || (ifname = mgrif->interfacename) == NULL) { PILLog(PIL_WARN, "bad parameters to IfForEachClientRemove"); return; } if ((clientt = g_hash_table_lookup(u->iftypes, ifname)) == NULL) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "Interface manager [%s/%s] has no clients" , PI_IFMANAGER, ifname); } return; }; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "IfForEachClientRemove(%s:%s)" , mgrt->typename, clientt->typename); } if (clientt->ifmgr_ref != mgrif) { PILLog(PIL_WARN, "Bad ifmgr_ref ptr in PILInterfaceType"); return; } g_hash_table_foreach_remove(clientt->interfaces, IfForEachClientHelper , &h); } static PIL_rc PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* commonops) { piinfo->pluginops = commonops; return PIL_OK; } static PIL_rc PILunregister_plugin(PILPlugin* piinfo) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PILunregister_plugin(%s)" , piinfo->plugin_name); } RemoveAPILPlugin(piinfo); return PIL_OK; } /* General logging function (not really UPPILS-specific) */ static void PILLog(PILLogLevel priority, const char * format, ...) { va_list args; GLogLevelFlags flags; switch(priority) { case PIL_FATAL: flags = G_LOG_LEVEL_ERROR; break; case PIL_CRIT: flags = G_LOG_LEVEL_CRITICAL; break; default: /* FALL THROUGH... */ case PIL_WARN: flags = G_LOG_LEVEL_WARNING; break; case PIL_INFO: flags = G_LOG_LEVEL_INFO; break; case PIL_DEBUG: flags = G_LOG_LEVEL_DEBUG; break; }; va_start (args, format); g_logv (G_LOG_DOMAIN, flags, format, args); va_end (args); } static const char * PIL_strerrmsgs [] = { "Success" , "Invalid Parameters" , "Bad plugin/interface type" , "Duplicate entry (plugin/interface name/type)" , "Oops happens" , "No such plugin/interface/interface type" }; const char * PIL_strerror(PIL_rc rc) { int irc = (int) rc; static char buf[128]; if (irc < 0 || irc >= DIMOF(PIL_strerrmsgs)) { snprintf(buf, sizeof(buf), "return code %d (?)", irc); return buf; } return PIL_strerrmsgs[irc]; } /* * Returns the PATHname of the file containing the requested plugin * This file handles PATH-like semantics from the rootdirlist. * It is also might be the right place to put alias handing in the future... */ static char * PILPluginPath(PILPluginUniv* universe, const char * plugintype , const char * pluginname) { char * PluginPath = NULL; char ** spath_component; for (spath_component = universe->rootdirlist; *spath_component ; ++ spath_component) { if (PluginPath) { g_free(PluginPath); PluginPath=NULL; } PluginPath = g_strdup_printf("%s%s%s%s%s%s" , *spath_component , G_DIR_SEPARATOR_S , plugintype , G_DIR_SEPARATOR_S , pluginname , PLUGINSUFFIX); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "PILS: Looking for %s/%s => [%s]" , plugintype, pluginname, PluginPath); } if (PluginExists(PluginPath) == PIL_OK) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "Plugin path for %s/%s => [%s]" , plugintype, pluginname, PluginPath); } return PluginPath; } /* FIXME: Put alias file processing here... */ } /* Can't find 'em all... */ return PluginPath; } static PIL_rc PluginExists(const char * PluginPath) { /* Make sure we can read and execute the plugin file */ /* This test is nice, because dlopen reasons aren't return codes */ if (access(PluginPath, R_OK) != 0) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Plugin file %s does not exist" , PluginPath); } return PIL_NOPLUGIN; } return PIL_OK; } /* Return PIL_OK if the given plugin exists */ PIL_rc PILPluginExists(PILPluginUniv* piuniv , const char * plugintype , const char * pluginname) { PIL_rc rc; char * path = PILPluginPath(piuniv, plugintype, pluginname); if (path == NULL) { return PIL_INVAL; } rc = PluginExists(path); DELETE(path); return rc; } /* * PILLoadPlugin() - loads a plugin into memory and calls the * initial() entry point in the plugin. * * * Method: * * Construct file name of plugin. * See if plugin exists. If not, fail with PIL_NOPLUGIN. * * Search Universe for plugin type * If found, search plugin type for pluginname * if found, fail with PIL_EXIST. * Otherwise, * Create new Plugin type structure * Use lt_dlopen() on plugin to get lt_dlhandle for it. * * Construct the symbol name of the initialization function. * * Use lt_dlsym() to find the pointer to the init function. * * Call the initialization function. */ PIL_rc PILLoadPlugin(PILPluginUniv* universe, const char * plugintype , const char * pluginname , void* plugin_user_data) { PIL_rc rc; char * PluginPath; char * PluginSym; PILPluginType* pitype; PILPlugin* piinfo; lt_dlhandle dlhand; PILPluginInitFun initfun; PluginPath = PILPluginPath(universe, plugintype, pluginname); if ((rc=PluginExists(PluginPath)) != PIL_OK) { DELETE(PluginPath); return rc; } if((pitype=g_hash_table_lookup(universe->PluginTypes, plugintype)) != NULL) { if ((piinfo = g_hash_table_lookup ( pitype->Plugins, pluginname)) != NULL) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Plugin %s already loaded" , PluginPath); } DELETE(PluginPath); return PIL_EXIST; } if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PluginType %s already present" , plugintype); } }else{ if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Creating PluginType for %s" , plugintype); } /* Create a new PILPluginType object */ pitype = NewPILPluginType(universe, plugintype); } g_assert(pitype != NULL); /* * At this point, we have a PILPluginType object and our * plugin name is not listed in it. */ dlhand = lt_dlopen(PluginPath); if (!dlhand) { PILLog(PIL_WARN , "lt_dlopen() failure on plugin %s/%s [%s]." " Reason: [%s]" , plugintype, pluginname , PluginPath , lt_dlerror()); DELETE(PluginPath); return PIL_NOPLUGIN; } DELETE(PluginPath); /* Construct the magic init function symbol name */ PluginSym = g_strdup_printf(PIL_FUNC_FMT , plugintype, pluginname); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Plugin %s/%s init function: %s" , plugintype, pluginname , PluginSym); } initfun = lt_dlsym(dlhand, PluginSym); if (initfun == NULL) { PILLog(PIL_WARN , "Plugin %s/%s init function (%s) not found" , plugintype, pluginname, PluginSym); DELETE(PluginSym); lt_dlclose(dlhand); dlhand=NULL; DelPILPluginType(pitype); return PIL_NOPLUGIN; } DELETE(PluginSym); /* * Construct the new PILPlugin object */ piinfo = NewPILPlugin(pitype, pluginname, dlhand, initfun); g_assert(piinfo != NULL); g_hash_table_insert(pitype->Plugins, g_strdup(piinfo->plugin_name), piinfo); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Plugin %s/%s loaded and constructed." , plugintype, pluginname); } if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Calling init function in plugin %s/%s." , plugintype, pluginname); } /* Save away the user_data for later */ piinfo->ud_plugin = plugin_user_data; /* initfun is allowed to change ud_plugin if they want */ initfun(piinfo, universe->imports, plugin_user_data); return PIL_OK; }/*PILLoadPlugin*/ #define REPORTERR(msg) PILLog(PIL_CRIT, "%s", msg) /* * Register an interface. * * This function is exported to plugins for their use. */ static PIL_rc PILRegisterInterface(PILPlugin* piinfo , const char * interfacetype /* Type of interface */ , const char * interfacename /* Name of interface */ , void* Ops /* Info (functions) exported by this interface */ , PILInterfaceFun close_func /* Close function for interface */ , PILInterface** interfaceid /* Interface id (OP) */ , void** Imports /* Functions imported by this interface (OP) */ , void* ud_interface /* Optional user_data */ ) { PILPluginUniv* piuniv; /* Universe this plugin is in */ PILPluginType* pitype; /* Type of this plugin */ PILInterfaceUniv* ifuniv; /* Universe this interface is in */ PILInterfaceType*iftype; /* Type of this interface */ PILInterface* ifinfo; /* Info about this Interface */ PILInterfaceType*ifmgrtype; /* PILInterfaceType for PI_IFMANAGER */ PILInterface* ifmgrinfo; /* Interf info for "interfacetype" */ const PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */ /* of type "interfacetype" */ PIL_rc rc; if ( piinfo == NULL || (pitype = piinfo->plugintype) == NULL || (piuniv = pitype->piuniv) == NULL || (ifuniv = piuniv->ifuniv) == NULL || ifuniv->iftypes == NULL ) { REPORTERR("bad parameters to PILRegisterInterface"); return PIL_INVAL; } /* Now we have lots of info, but not quite enough... */ if ((iftype = g_hash_table_lookup(ifuniv->iftypes, interfacetype)) == NULL) { /* Try to autoload the needed interface handler */ rc = PILLoadPlugin(piuniv, PI_IFMANAGER, interfacetype, NULL); /* See if the interface handler loaded like we expect */ if ((iftype = g_hash_table_lookup(ifuniv->iftypes , interfacetype)) == NULL) { return PIL_BADTYPE; } } if ((ifinfo = g_hash_table_lookup(iftype->interfaces, interfacename)) != NULL) { g_warning("Attempt to register duplicate interface: %s/%s" , interfacetype, interfacename); return PIL_EXIST; } /* * OK... Now we know it is valid, and isn't registered... * Let's locate the InterfaceManager registrar for this type */ if ((ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER)) == NULL) { REPORTERR("No " PI_IFMANAGER " type!"); return PIL_OOPS; } if ((ifmgrinfo = g_hash_table_lookup(ifmgrtype->interfaces , interfacetype)) == NULL) { PILLog(PIL_CRIT , "No interface manager for given type (%s) !" , interfacetype); return PIL_BADTYPE; } ifops = ifmgrinfo->exports; /* Now we have all the information anyone could possibly want ;-) */ ifinfo = NewPILInterface(iftype, interfacename, Ops , close_func, ud_interface, piinfo); g_assert(ifmgrinfo == ifinfo->ifmanager); *interfaceid = ifinfo; /* Call the registration function for our interface type */ rc = ifops->RegisterInterface(ifinfo, Imports); /* Increment reference count of interface manager */ IfIncrRefCount(ifmgrinfo, 1); /* Increment the ref count of the plugin that loaded us */ PluginIncrRefCount(piinfo, 1); if (rc != PIL_OK) { RemoveAPILInterface(ifinfo); } return rc; } /* * Method: * * Verify interface is valid. * * Call interface close function. * * Call interface manager unregister function * * Call RmAPILInterface to remove from InterfaceType table, and * free interface object. * */ static PIL_rc PILunregister_interface(PILInterface* id) { PILInterfaceType* t; PILInterfaceUniv* u; PIL_rc rc; PILInterface* ifmgr_info; /* Pointer to our interface handler */ const PILInterfaceOps* exports; /* InterfaceManager operations for * the type of interface we are */ if ( id == NULL || (t = id->interfacetype) == NULL || (u = t->universe) == NULL || id->interfacename == NULL) { PILLog(PIL_WARN, "PILunregister_interface: bad interfaceid"); return PIL_INVAL; } if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PILunregister_interface(%s/%s)" , t->typename, id->interfacename); } PILValidateInterface(NULL, id, t); PILValidateInterfaceType(NULL, t, u); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "Calling InterfaceClose on %s/%s" , t->typename, id->interfacename); } /* Call the close function supplied by the interface */ if ((id->if_close != NULL) && ((rc=id->if_close(id, id->ud_interface)) != PIL_OK)) { PILLog(PIL_WARN, "InterfaceClose on %s/%s returned %s" , t->typename, id->interfacename , PIL_strerror(rc)); } else { rc = PIL_OK; } /* Find the InterfaceManager that manages us */ ifmgr_info = t->ifmgr_ref; g_assert(ifmgr_info != NULL); /* Find the exported functions from that IFIF */ exports = ifmgr_info->exports; g_assert(exports != NULL && exports->UnRegisterInterface != NULL); /* Call the interface manager unregister function */ exports->UnRegisterInterface(id); /* Decrement reference count of interface manager */ IfIncrRefCount(ifmgr_info, -1); /* This may make ifmgr_info invalid */ ifmgr_info = NULL; /* Decrement the reference count of the plugin that loaded us */ PluginIncrRefCount(id->loadingpi, -1); return rc; } static PILInterfaceUniv* NewPILInterfaceUniv(PILPluginUniv* piuniv) { PILInterfaceUniv* ret = NEW(PILInterfaceUniv); static int ltinityet = 0; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "NewPILInterfaceUniv(0x%x)" , (unsigned long)ret); } if (!ltinityet) { ltinityet=1; lt_dlinit(); } STATNEW(interfaceuniv); ret->MagicNum = PIL_MAGIC_INTERFACEUNIV; /* Make the two universes point at each other */ ret->piuniv = piuniv; piuniv->ifuniv = ret; ret->iftypes = g_hash_table_new(g_str_hash, g_str_equal); InterfaceManager_plugin_init(piuniv); return ret; } static void DelPILInterfaceUniv(PILInterfaceUniv* ifuniv) { PILInterfaceType* ifmgrtype; g_assert(ifuniv!= NULL && ifuniv->iftypes != NULL); PILValidateInterfaceUniv(NULL, ifuniv, NULL); STATFREE(interfaceuniv); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILInterfaceUniv(0x%lx)" , (unsigned long) ifuniv); } g_hash_table_foreach_remove(ifuniv->iftypes, RmAPILInterfaceType, NULL); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "DelPILInterfaceUniv: final cleanup"); } ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER); RemoveAPILInterfaceType(ifmgrtype, ifmgrtype); /* * FIXME! need to delete the interface for PI_IFMANAGER last * Right now, it seems to happen last, but I think that's * coincidence... */ g_hash_table_destroy(ifuniv->iftypes); ZAP(ifuniv); DELETE(ifuniv); } /* * These RmA* functions primarily called from hash_table_foreach, * so they have gpointer arguments. This *not necessarily* clause * is why they do the g_hash_table_lookup_extended call instead of * just deleting the key. When called from outside, the key * may not be pointing at the key to actually free, but a copy * of the key. */ static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */ RmAPILInterfaceType ( gpointer typename /* Name of this interface type */ , gpointer iftype /* PILInterfaceType* */ , gpointer notused ) { PILInterfaceType* Iftype = iftype; PILInterfaceUniv* Ifuniv = Iftype->universe; /* * We are not always called by g_hash_table_foreach_remove() */ g_assert(IS_PILINTERFACETYPE(Iftype)); PILValidateInterfaceUniv(NULL, Ifuniv, NULL); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILInterfaceType(%s)" , (char*)typename); } if (iftype != notused && strcmp(Iftype->typename, PI_IFMANAGER) == 0) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RmAPILInterfaceType: skipping (%s)" , (char*)typename); } return FALSE; } DelPILInterfaceType(iftype); DELETE(typename); return TRUE; } static void RemoveAPILInterfaceType(PILInterfaceType*Iftype, PILInterfaceType* t2) { PILInterfaceUniv* Ifuniv = Iftype->universe; gpointer key; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "RemoveAPILInterfaceType(%s)" , Iftype->typename); } if (t2 != Iftype && strcmp(Iftype->typename, PI_IFMANAGER) == 0) { PILLog(PIL_DEBUG, "RemoveAPILInterfaceType: skipping (%s)" , Iftype->typename); return; } if (g_hash_table_lookup_extended(Ifuniv->iftypes , Iftype->typename, &key, (gpointer)&Iftype)) { g_hash_table_remove(Ifuniv->iftypes, key); RmAPILInterfaceType(key, Iftype, t2); }else{ g_assert_not_reached(); } } /* * We need to write more functions: These include... * * Plugin functions: * * PILPluginPath() - returns path name for a given plugin * * PILPluginTypeList() - returns list of plugins of a given type * */ static void free_dirlist(struct dirent** dlist, int n); static int qsort_string_cmp(const void *a, const void *b); static void free_dirlist(struct dirent** dlist, int n) { int j; for (j=0; j < n; ++j) { if (dlist[j]) { free(dlist[j]); dlist[j] = NULL; } } free(dlist); } static int qsort_string_cmp(const void *a, const void *b) { return(strcmp(*(const char * const *)a, *(const char * const *)b)); } #define FREE_DIRLIST(dlist, n) {free_dirlist(dlist, n); dlist = NULL;} static int so_select (const struct dirent *dire) { const char obj_end [] = PLUGINSUFFIX; const char *end = &dire->d_name[strlen(dire->d_name) - (STRLEN_CONST(obj_end))]; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "In so_select: %s.", dire->d_name); } if (end < dire->d_name) { return 0; } if (strcmp(end, obj_end) == 0) { if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "FILE %s looks like a plugin name." , dire->d_name); } return 1; } if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "FILE %s Doesn't look like a plugin name [%s] " "%d %d %s." , dire->d_name, end , sizeof(obj_end), strlen(dire->d_name) , &dire->d_name[strlen(dire->d_name) - (STRLEN_CONST(obj_end))]); } return 0; } /* Return (sorted) list of available plugin names */ static char** PILPluginTypeListPlugins(PILPluginType* pitype , int * picount /* Can be NULL ... */) { const char * piclass = pitype->plugintype; unsigned plugincount = 0; char ** result = NULL; int initoff = 0; char ** pelem; /* Return all the plugins in all the directories in the PATH */ for (pelem=pitype->piuniv->rootdirlist; *pelem; ++pelem) { int j; GString* path; int dircount; struct dirent** files; path = g_string_new(*pelem); g_assert(piclass != NULL); if (piclass) { if (g_string_append_c(path, G_DIR_SEPARATOR) == NULL || g_string_append(path, piclass) == NULL) { g_string_free(path, 1); path = NULL; return(NULL); } } files = NULL; errno = 0; dircount = scandir(path->str, &files , SCANSEL_CAST so_select, NULL); if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PILS: Examining directory [%s]" ": [%d] files matching [%s] suffix found." , path->str, dircount, PLUGINSUFFIX); } g_string_free(path, 1); path=NULL; if (dircount <= 0) { if (files != NULL) { FREE_DIRLIST(files, dircount); files = NULL; } if (DEBUGPLUGIN) { PILLog(PIL_DEBUG , "PILS: skipping empty directory" " in PILPluginTypeListPlugins()"); } continue; } initoff = plugincount; plugincount += dircount; if (result == NULL) { result = (char **) g_malloc((plugincount+1)*sizeof(char *)); }else{ result = (char **) g_realloc(result , (plugincount+1)*sizeof(char *)); } for (j=0; j < dircount; ++j) { char* s; unsigned slen = strlen(files[j]->d_name) - STRLEN_CONST(PLUGINSUFFIX); s = g_malloc(slen+1); strncpy(s, files[j]->d_name, slen); s[slen] = EOS; result[initoff+j] = s; if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PILS: plugin [%s] found" , s); } } FREE_DIRLIST(files, dircount); files = NULL; } if (picount != NULL) { *picount = plugincount; } if (result) { result[plugincount] = NULL; /* Return them in sorted order... */ qsort(result, plugincount, sizeof(char *), qsort_string_cmp); }else{ if (DEBUGPLUGIN) { PILLog(PIL_DEBUG, "PILS: NULL return" " from PILPluginTypeListPlugins()"); } } return result; } /* Return (sorted) list of available plugin names */ char** PILListPlugins(PILPluginUniv* u, const char * pitype , int * picount /* Can be NULL ... */) { PILPluginType* t; if ((t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL) { if (picount) { *picount = 0; } t = NewPILPluginType(u, pitype); if (!t) { return NULL; } } return PILPluginTypeListPlugins(t, picount); } void PILFreePluginList(char ** pluginlist) { char ** ml = pluginlist; if (!ml) { return; } while (*ml != NULL) { DELETE(*ml); } DELETE(pluginlist); } static void PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype) { const char * Key = key; const PILPlugin * Plugin = plugin; g_assert(IS_PILPLUGIN(Plugin)); g_assert(Key == NULL || strcmp(Key, Plugin->plugin_name) == 0); g_assert (Plugin->refcnt >= 0 ); /* g_assert (Plugin->pluginops != NULL ); */ g_assert (strcmp(Key, PI_IFMANAGER) == 0 || Plugin->dlinitfun != NULL ); g_assert (strcmp(Plugin->plugin_name, PI_IFMANAGER) == 0 || Plugin->dlhandle != NULL); g_assert(Plugin->plugintype != NULL); g_assert(IS_PILPLUGINTYPE(Plugin->plugintype)); g_assert(pitype == NULL || pitype == Plugin->plugintype); } static void PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv) { char * Key = key; PILPluginType * Pitype = pitype; PILPluginUniv * Muniv = piuniv; g_assert(IS_PILPLUGINTYPE(Pitype)); g_assert(Muniv == NULL || IS_PILPLUGINUNIV(Muniv)); g_assert(Key == NULL || strcmp(Key, Pitype->plugintype) == 0); g_assert(IS_PILPLUGINUNIV(Pitype->piuniv)); g_assert(piuniv == NULL || piuniv == Pitype->piuniv); g_assert(Pitype->Plugins != NULL); g_hash_table_foreach(Pitype->Plugins, PILValidatePlugin, Pitype); } static void PILValidatePluginUniv(gpointer key, gpointer piuniv, gpointer dummy) { PILPluginUniv * Muniv = piuniv; g_assert(IS_PILPLUGINUNIV(Muniv)); g_assert(Muniv->rootdirlist != NULL); g_assert(Muniv->imports != NULL); g_hash_table_foreach(Muniv->PluginTypes, PILValidatePluginType, piuniv); PILValidateInterfaceUniv(NULL, Muniv->ifuniv, piuniv); } static void PILValidateInterface(gpointer key, gpointer interface, gpointer iftype) { char * Key = key; PILInterface* Interface = interface; g_assert(IS_PILINTERFACE(Interface)); g_assert(Key == NULL || strcmp(Key, Interface->interfacename) == 0); g_assert(IS_PILINTERFACETYPE(Interface->interfacetype)); g_assert(iftype == NULL || iftype == Interface->interfacetype); g_assert(Interface->ifmanager!= NULL); g_assert(IS_PILINTERFACE(Interface->ifmanager)); g_assert(strcmp(Interface->interfacetype->typename , Interface->ifmanager->interfacename)== 0); g_assert(Interface->exports != NULL); } static void PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv) { char * Key = key; PILInterfaceType* Iftype = iftype; g_assert(IS_PILINTERFACETYPE(Iftype)); g_assert(Key == NULL || strcmp(Key, Iftype->typename) == 0); g_assert(ifuniv == NULL || Iftype->universe == ifuniv); g_assert(Iftype->interfaces != NULL); g_assert(Iftype->ifmgr_ref != NULL); g_assert(IS_PILINTERFACE(Iftype->ifmgr_ref)); g_assert(Key == NULL || strcmp(Key, Iftype->ifmgr_ref->interfacename) == 0); g_hash_table_foreach(Iftype->interfaces, PILValidateInterface, iftype); } static void PILValidateInterfaceUniv(gpointer key, gpointer ifuniv, gpointer piuniv) { PILInterfaceUniv* Ifuniv = ifuniv; PILPluginUniv* Pluginuniv = piuniv; g_assert(IS_PILINTERFACEUNIV(Ifuniv)); g_assert(Pluginuniv == NULL || IS_PILPLUGINUNIV(Pluginuniv)); g_assert(piuniv == NULL || piuniv == Ifuniv->piuniv); g_hash_table_foreach(Ifuniv->iftypes, PILValidateInterfaceType, ifuniv); } #define PRSTAT(type) { \ PILLog(PIL_INFO, "Plugin system objects (" #type "): " \ "\tnew %ld free \%ld current %ld" \ , PILstats.type.news \ , PILstats.type.frees \ , PILstats.type.news - PILstats.type.frees); \ } void PILLogMemStats(void) { PRSTAT(plugin); PRSTAT(pitype); PRSTAT(piuniv); PRSTAT(interface); PRSTAT(interfacetype); PRSTAT(interfaceuniv); } /* * Function for logging with the given logging function * The reason why it's here is so we can get printf arg checking * You can't get that when you call a function pointer directly. */ void PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...) { va_list args; char * str; int err = errno; va_start (args, fmt); str = g_strdup_vprintf(fmt, args); va_end (args); logfun(priority, "%s", str); g_free(str); errno = err; } Reusable-Cluster-Components-glue--3cff550e1084/lib/pils/test.c0000644000000000000000000000553312120057602024127 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * Sample Interface manager. */ #define PIL_PLUGINTYPE test #define PIL_PLUGINTYPENAME "test" #define PIL_PLUGIN test #define PIL_PLUGINNAME "test" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL /* We are a interface manager... */ #define ENABLE_PLUGIN_MANAGER_PRIVATE #include PIL_PLUGIN_BOILERPLATE("1.0", DebugFlag, Ourclose) /* * Places to store information gotten during registration. */ static const PILPluginImports* OurPIImports; /* Imported plugin funs */ static PILPlugin* OurPlugin; /* Our plugin info */ static PILInterfaceImports* OurIfImports; /* Interface imported funs */ static PILInterface* OurIf; /* Pointer to interface info */ static void Ourclose (PILPlugin* us) { } /* * Our Interface Manager interfaces - exported to the universe! * * (or at least the interface management universe ;-). * */ static PILInterfaceOps OurIfOps = { /* FIXME -- put some in here !! */ }; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*); static PIL_rc IfClose(PILInterface*intf, void* ud_interface) { OurPIImports->log(PIL_INFO, "In Ifclose (test plugin)"); return PIL_OK; } PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr) { PIL_rc ret; /* * Force compiler to check our parameters... */ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun; OurPIImports = imports; OurPlugin = us; imports->log(PIL_INFO, "Plugin %s: user_ptr = %lx" , PIL_PLUGINNAME, (unsigned long)user_ptr); imports->log(PIL_INFO, "Registering ourselves as a plugin"); /* Register as a plugin */ imports->register_plugin(us, &OurPIExports); imports->log(PIL_INFO, "Registering our interfaces"); /* Register our interfaces */ ret = imports->register_interface ( us , PIL_PLUGINTYPENAME , PIL_PLUGINNAME , &OurIfOps /* Exported interface operations */ , IfClose /* Interface Close function */ , &OurIf , (void*)&OurIfImports , NULL); imports->log(PIL_INFO, "test init function: returning %d" , ret); return ret; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/InterfaceMgr/HBauth.c0000644000000000000000000001124512120057602027400 0ustar00usergroup00000000000000/* * Heartbeat authentication interface manager * * Copyright 2001 Alan Robertson * Licensed under the GNU Lesser General Public License * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ */ #define PIL_PLUGINTYPE InterfaceMgr #define PIL_PLUGIN HBauth #define PIN(f) #f #define PIN2(f) PIN(f) #define PIN3 PIN2(PIL_PLUGIN) #define PIT PIN2(PIL_PLUGINTYPE) /* We are a interface manager... */ #define ENABLE_PLUGIN_MANAGER_PRIVATE #include #include #include PIL_PLUGIN_BOILERPLATE2("1.0", AuthDebugFlag) /* * Places to store information gotten during registration. */ static const PILPluginImports* AuthPIImports; /* Imported plugin fcns */ static PILPlugin* AuthPlugin; /* Our plugin info */ static PILInterfaceImports* AuthIfImports; /* Interface imported fcns */ static PILInterface* AuthIf; /* Our Auth Interface info */ /* Our exported auth interface management functions */ static PIL_rc RegisterAuthIF(PILInterface* ifenv, void** imports); static PIL_rc UnregisterAuthIF(PILInterface*iifinfo); /* * Our Interface Manager interfaces - exported to the universe! * * (or at least to the interface management universe ;-). * * These are the interfaces which are used to manage our * client authentication interfaces * */ static PILInterfaceOps AuthIfOps = { RegisterAuthIF , UnregisterAuthIF }; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*); /* * Our user_ptr is presumed to point at a GHashTable for us * to put plugin into when they show up, and drop from when * they disappear. * * We need to think more carefully about the way for us to get * the user_ptr from the global environment. * * We need to think more carefully about how interface registration * etc. interact with plugin loading, reference counts, etc. and how * the application that uses us (i.e., heartbeat) interacts with us. * * Issues include: * - freeing all memory, * - making sure things are all cleaned up correctly * - Thread-safety? * * I think the global system should handle thread-safety. */ PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr) { PIL_rc ret; /* * Force compiler to check our parameters... */ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun; if (user_ptr == NULL) { imports->log(PIL_CRIT , "Interface Manager %s requires non-NULL " " user pointer (to GHashTable) at initialization" , PIN3); return PIL_INVAL; } AuthPIImports = imports; AuthPlugin = us; /* Register as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interfaces */ ret = imports->register_interface(us , PIT , PIN3 , &AuthIfOps , NULL , &AuthIf /* Our interface object pointer */ , (void**)&AuthIfImports /* Interface-imported functions */ , user_ptr); return ret; } /* * We get called for every authentication interface that gets registered. * * It's our job to make the authentication interface that's * registering with us available to the system. * * We do that by adding it to a g_hash_table of authentication * plugin. The rest of the system takes it from there... * The key is the authentication method, and the data * is a pointer to the functions the method exports. * It's a piece of cake ;-) */ static PIL_rc RegisterAuthIF(PILInterface* intf, void** imports) { GHashTable* authtbl = intf->ifmanager->ud_interface; g_assert(authtbl != NULL); /* Reference count should now be one */ g_assert(intf->refcnt == 1); g_hash_table_insert(authtbl, intf->interfacename, intf->exports); return PIL_OK; } /* Unregister a client authentication interface - * We get called from the interface mgmt sys when someone requests that * a interface be unregistered. */ static PIL_rc UnregisterAuthIF(PILInterface*intf) { GHashTable* authtbl = intf->ifmanager->ud_interface; g_assert(authtbl != NULL); intf->refcnt--; g_assert(intf->refcnt >= 0); if (intf->refcnt <= 0) { g_hash_table_remove(authtbl, intf->interfacetype); } return PIL_OK; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/InterfaceMgr/Makefile.am0000644000000000000000000000242012120057602030110 0ustar00usergroup00000000000000# # InterfaceMgr: Interface manager plugins for Linux-HA # # Copyright (C) 2001 Alan Robertson # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls ## libraries plugindir = $(libdir)/@HB_PKG@/plugins/InterfaceMgr plugin_LTLIBRARIES = generic.la generic_la_SOURCES = generic.c generic_la_LDFLAGS = -export-dynamic -module -avoid-version Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/InterfaceMgr/generic.c0000644000000000000000000003035212120057602027641 0ustar00usergroup00000000000000/* * * Generic interface (implementation) manager * * Copyright 2001 Alan Robertson * Licensed under the GNU Lesser General Public License * * This manager will manage any number of types of interfaces. * * This means that when any implementations of our client interfaces register * or unregister, it is us that makes their interfaces show up in the outside * world. * * And, of course, we have to do this in a very generic way, since we have * no idea about the client programs or interface types, or anything else. * * We do that by getting a parameter passed to us which tell us the names * of the interface types we want to manage, and the address of a GHashTable * for each type that we put the implementation in when they register * themselves. * * So, each type of interface that we manage gets its own private * GHashTable of the implementations of that type that are currently * registered. * * For example, if we manage communication modules, their exported * interfaces will be registered in a hash table. If we manage * authentication modules, they'll have their (separate) hash table that * their exported interfaces are registered in. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define PIL_PLUGINTYPE InterfaceMgr #define PIL_PLUGINTYPE_S "InterfaceMgr" #define PIL_PLUGIN generic #define PIL_PLUGIN_S "generic" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL /* We are an interface manager... */ #define ENABLE_PLUGIN_MANAGER_PRIVATE #define ENABLE_PIL_DEFS_PRIVATE #include #include #include PIL_PLUGIN_BOILERPLATE("1.0", GenDebugFlag, CloseGeneralPluginManager) /* * Key is interface type, value is a PILGenericIfMgmtRqst. * The key is g_strdup()ed, but the struct is not copied. */ static gboolean FreeAKey(gpointer key, gpointer value, gpointer data); /* * Places to store information gotten during registration. */ static const PILPluginImports* GenPIImports; /* Imported plugin fcns */ static PILPlugin* GenPlugin; /* Our plugin info */ static PILInterfaceImports* GenIfImports; /* Interface imported fcns */ /* Our exported generic interface management functions */ static PIL_rc RegisterGenIF(PILInterface* ifenv, void** imports); static PIL_rc UnregisterGenIF(PILInterface*iifinfo); static PIL_rc CloseGenInterfaceManager(PILInterface*, void* info); /* * Our Interface Manager interfaces - exported to the universe! * * (or at least to the interface management universe ;-). * * These are the interfaces which are used to manage our * client implementations */ static PILInterfaceOps GenIfOps = { RegisterGenIF , UnregisterGenIF }; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*); /* * Our user_ptr is presumed to point to NULL-terminated array of * PILGenericIfMgmtRqst structs. * * These requests have pointers to GHashTables for us * to put plugins into when they show up, and drop from when * they disappear. * * Issues include: * - freeing all memory, * - making sure things are all cleaned up correctly * - Thread-safety? * * IMHO the global system should handle thread-safety. */ static PIL_rc AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr) { PIL_rc ret; PILGenericIfMgmtRqst* user_req; PILGenericIfMgmtRqst* curreq; GHashTable* MasterTable = NULL; /* * Force the compiler to check our parameters... */ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun; GenPIImports = imports; if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "IF manager %s: initializing.", PIL_PLUGIN_S); } if (user_ptr == NULL) { PILCallLog(GenPIImports->log, PIL_CRIT , "%s Interface Manager requires non-NULL " " PILGenericIfMgmtRqst user pointer at initialization." , PIL_PLUGIN_S); return PIL_INVAL; } GenPlugin = us; if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "IF manager %s: registering as a plugin." , PIL_PLUGIN_S); } user_req = user_ptr; MasterTable = g_hash_table_new(g_str_hash, g_str_equal); us->ud_plugin = MasterTable; /* Override passed value */ /* Register ourselves as a plugin */ if ((ret = imports->register_plugin(us, &OurPIExports)) != PIL_OK) { PILCallLog(imports->log, PIL_CRIT , "IF manager %s unable to register as plugin (%s)" , PIL_PLUGIN_S, PIL_strerror(ret)); return ret; } /* * Register to manage implementations * for all the interface types we've been asked to manage. */ for(curreq = user_req; curreq->iftype != NULL; ++curreq) { PIL_rc newret; newret = AddAnInterfaceType(us, MasterTable, curreq); if (newret != PIL_OK) { ret = newret; } } /* * Our plugin and all our registered plugin types * have ud_plugin pointing at MasterTable. */ return ret; } static PIL_rc AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req) { PIL_rc rc; PILInterface* GenIf; /* Our Generic Interface info*/ g_assert(MasterTable != NULL); g_hash_table_insert(MasterTable, g_strdup(req->iftype), req); if (req->ifmap == NULL) { PILCallLog(GenPIImports->log, PIL_CRIT , "IF manager %s: iftype %s has NULL" " ifmap pointer address." , PIL_PLUGIN_S, req->iftype); return PIL_INVAL; } if ((*req->ifmap) != NULL) { PILCallLog(GenPIImports->log, PIL_CRIT , "IF manager %s: iftype %s GHashTable pointer" " was not initialized to NULL" , PIL_PLUGIN_S, req->iftype); return PIL_INVAL; } if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "IF manager %s: registering ourselves" " to manage interface type %s" , PIL_PLUGIN_S, req->iftype); PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: ifmap: 0x%lx callback: 0x%lx" " imports: 0x%lx" , PIL_PLUGIN_S , (unsigned long)req->ifmap , (unsigned long)req->callback , (unsigned long)req->importfuns); } /* Create the hash table to communicate with this client */ *(req->ifmap) = g_hash_table_new(g_str_hash, g_str_equal); rc = GenPIImports->register_interface(us , PIL_PLUGINTYPE_S , req->iftype /* the iftype we're managing here */ , &GenIfOps , CloseGenInterfaceManager , &GenIf , (void*)&GenIfImports , MasterTable); /* Point ud_interface to MasterTable */ /* We don't ever want to be unloaded... */ GenIfImports->ModRefCount(GenIf, +100); if (rc != PIL_OK) { PILCallLog(GenPIImports->log, PIL_CRIT , "Generic interface manager %s: unable to register" " to manage interface type %s: %s" , PIL_PLUGIN_S, req->iftype , PIL_strerror(rc)); } return rc; } static void CloseGeneralPluginManager(PILPlugin* us) { GHashTable* MasterTable = us->ud_plugin; int count; g_assert(MasterTable != NULL); /* * All our clients have already been shut down automatically * This is the final shutdown for us... */ /* There *shouldn't* be any keys in there ;-) */ if ((count=g_hash_table_size(MasterTable)) > 0) { /* But just in case there are... */ g_hash_table_foreach_remove(MasterTable, FreeAKey, NULL); } g_hash_table_destroy(MasterTable); us->ud_plugin = NULL; return; } /* * We get called for every time an implementation registers itself as * implementing one of the kinds of interfaces we manage. * * It's our job to make the implementation that's * registering with us available to the system. * * We do that by adding it to a GHashTable for its interface type * Our users in the rest of the system takes it from there... * * The key to the GHashTable is the implementation name, and the data is * a pointer to the information the implementation exports. * * It's a piece of cake ;-) */ static PIL_rc RegisterGenIF(PILInterface* intf, void** imports) { PILGenericIfMgmtRqst* ifinfo; GHashTable* MasterTable = intf->ifmanager->ud_interface; g_assert(MasterTable != NULL); /* Reference count should now be one */ if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: interface %s/%s registering." , PIL_PLUGIN_S, intf->interfacetype->typename , intf->interfacename); } g_assert(intf->refcnt == 1); /* * We need to add it to the table that goes with this particular * type of interface. */ if ((ifinfo = g_hash_table_lookup(MasterTable , intf->interfacetype->typename)) != NULL) { GHashTable* ifmap = *(ifinfo->ifmap); g_hash_table_insert(ifmap, intf->interfacename,intf->exports); if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: Inserted interface [%s] in hash" " table @ 0x%08lx" , PIL_PLUGIN_S, intf->interfacename , (unsigned long)ifmap); PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: Exports are here: 0x%08x" , PIL_PLUGIN_S , GPOINTER_TO_UINT(intf->exports)); } if (ifinfo->callback != NULL) { PILInterfaceType* t = intf->interfacetype; if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: callback 0x%lx" , PIL_PLUGIN_S , (unsigned long)ifinfo->callback); } ifinfo->callback(PIL_REGISTER , t->universe->piuniv, intf->interfacename , t->typename, ifinfo->userptr); } *imports = ifinfo->importfuns; return PIL_OK; }else{ PILCallLog(GenPIImports->log, PIL_WARN , "RegisterGenIF: interface type %s not found" , intf->interfacename); } return PIL_INVAL; } /* Unregister an implementation - * We get called from the interface management system when someone * has requested that an implementation of a client interface be * unregistered. */ static PIL_rc UnregisterGenIF(PILInterface*intf) { GHashTable* MasterTable = intf->ifmanager->ud_interface; PILGenericIfMgmtRqst* ifinfo; g_assert(MasterTable != NULL); g_assert(intf->refcnt >= 0); /* * Go through the "master table" and find client table, * notify client we're about to remove this entry, then * then remove this entry from it. */ if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: unregistering interface %s/%s." , PIL_PLUGIN_S, intf->interfacetype->typename , intf->interfacename); } if ((ifinfo = g_hash_table_lookup(MasterTable , intf->interfacetype->typename)) != NULL) { GHashTable* ifmap = *(ifinfo->ifmap); if (ifinfo->callback != NULL) { PILInterfaceType* t = intf->interfacetype; if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_DEBUG , "%s IF manager: callback 0x%lx" , PIL_PLUGIN_S , (unsigned long)ifinfo->callback); } ifinfo->callback(PIL_UNREGISTER , t->universe->piuniv, intf->interfacename , t->typename, ifinfo->userptr); } /* Remove the client entry from master table */ g_hash_table_remove(ifmap, intf->interfacename); }else{ PILCallLog(GenPIImports->log, PIL_WARN , "UnregisterGenIF: interface type %s not found" , intf->interfacename); return PIL_INVAL; } return PIL_OK; } /* * Close down the generic interface manager. */ static PIL_rc CloseGenInterfaceManager(PILInterface*intf, void* info) { void* key; void* data; GHashTable* MasterTable = intf->ud_interface; if (GenDebugFlag) { PILCallLog(GenPIImports->log, PIL_INFO , "In CloseGenInterFaceManager on %s/%s (MasterTable: 0x%08lx)" , intf->interfacetype->typename, intf->interfacename , (unsigned long)MasterTable); } g_assert(MasterTable != NULL); if (g_hash_table_lookup_extended(MasterTable , intf->interfacename, &key, &data)) { PILGenericIfMgmtRqst* ifinfo = data; g_hash_table_destroy(*(ifinfo->ifmap)); *(ifinfo->ifmap) = NULL; g_hash_table_remove(MasterTable, key); g_free(key); }else{ g_assert_not_reached(); } return PIL_OK; } static gboolean FreeAKey(gpointer key, gpointer value, gpointer data) { g_free(key); return TRUE; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/Makefile.am0000644000000000000000000000147312120057602025551 0ustar00usergroup00000000000000# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = InterfaceMgr stonith lrm compress Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/compress/Makefile.am0000644000000000000000000000324712120057602027405 0ustar00usergroup00000000000000# # InterfaceMgr: Interface manager plugins for Linux-HA # # Copyright (C) 2001 Alan Robertson # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in if BUILD_ZLIB_COMPRESS_MODULE zlibmodule = zlib.la endif if BUILD_BZ2_COMPRESS_MODULE bz2module = bz2.la endif SUBDIRS = INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls AM_CFLAGS = @CFLAGS@ ## libraries halibdir = $(libdir)/@HB_PKG@ plugindir = $(halibdir)/plugins/compress plugin_LTLIBRARIES = $(zlibmodule) $(bz2module) zlib_la_SOURCES = zlib.c zlib_la_LDFLAGS = -export-dynamic -module -avoid-version -lz zlib_la_LIBADD = $(top_builddir)/replace/libreplace.la bz2_la_SOURCES = bz2.c bz2_la_LDFLAGS = -export-dynamic -module -avoid-version -lbz2 bz2_la_LIBADD = $(top_builddir)/replace/libreplace.la Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/compress/bz2.c0000644000000000000000000000730012120057602026204 0ustar00usergroup00000000000000 /* bz2.c: compression module using bz2 for heartbeat. * * Copyright (C) 2005 Guochun Shi * * SECURITY NOTE: It would be very easy for someone to masquerade as the * device that you're pinging. If they don't know the password, all they can * do is echo back the packets that you're sending out, or send out old ones. * This does mean that if you're using such an approach, that someone could * make you think you have quorum when you don't during a cluster partition. * The danger in that seems small, but you never know ;-) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define PIL_PLUGINTYPE HB_COMPRESS_TYPE #define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S #define PIL_PLUGIN bz2 #define PIL_PLUGIN_S "bz2" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include #include #include #include #include #include static struct hb_compress_fns bz2Ops; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static struct hb_media_imports* OurImports; static void* interfprivate; #define LOG PluginImports->log #define MALLOC PluginImports->alloc #define STRDUP PluginImports->mstrdup #define FREE PluginImports->mfree PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &bz2Ops , NULL /*close */ , &OurInterface , (void*)&OurImports , interfprivate); } static int bz2_compress(char* dest, size_t* destlen, const char* _src, size_t srclen) { int ret; char* src; unsigned int tmpdestlen; memcpy(&src, &_src, sizeof(char*)); tmpdestlen = *destlen; ret = BZ2_bzBuffToBuffCompress(dest, &tmpdestlen, src, srclen, 1, 0, 30); if (ret != BZ_OK){ cl_log(LOG_ERR, "%s: compression failed", __FUNCTION__); return HA_FAIL; } *destlen = tmpdestlen; return HA_OK; } static int bz2_decompress(char* dest, size_t* destlen, const char* _src, size_t srclen) { int ret; char* src; unsigned int tmpdestlen; memcpy(&src, &_src, sizeof(char*)); tmpdestlen = *destlen; ret = BZ2_bzBuffToBuffDecompress(dest, &tmpdestlen, src, srclen, 1, 0); if (ret != BZ_OK){ cl_log(LOG_ERR, "%s: decompression failed", __FUNCTION__); return HA_FAIL; } *destlen = tmpdestlen; return HA_OK; } static const char* bz2_getname(void) { return "bz2"; } static struct hb_compress_fns bz2Ops ={ bz2_compress, bz2_decompress, bz2_getname, }; Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/compress/zlib.c0000644000000000000000000000707312120057602026456 0ustar00usergroup00000000000000 /* zlib.c: compression module using zlib for heartbeat. * * Copyright (C) 2005 Guochun Shi * * SECURITY NOTE: It would be very easy for someone to masquerade as the * device that you're pinging. If they don't know the password, all they can * do is echo back the packets that you're sending out, or send out old ones. * This does mean that if you're using such an approach, that someone could * make you think you have quorum when you don't during a cluster partition. * The danger in that seems small, but you never know ;-) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define PIL_PLUGINTYPE HB_COMPRESS_TYPE #define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S #define PIL_PLUGIN zlib #define PIL_PLUGIN_S "zlib" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include #include #include #include #include static struct hb_compress_fns zlibOps; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static struct hb_media_imports* OurImports; static void* interfprivate; #define LOG PluginImports->log #define MALLOC PluginImports->alloc #define STRDUP PluginImports->mstrdup #define FREE PluginImports->mfree PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &zlibOps , NULL /*close */ , &OurInterface , (void*)&OurImports , interfprivate); } static int zlib_compress(char* dest, size_t* _destlen, const char* src, size_t _srclen) { int ret; uLongf destlen = *_destlen; uLongf srclen = _srclen; ret = compress((Bytef *)dest, &destlen, (const Bytef *)src, srclen); if (ret != Z_OK){ cl_log(LOG_ERR, "%s: compression failed", __FUNCTION__); return HA_FAIL; } *_destlen = destlen; return HA_OK; } static int zlib_decompress(char* dest, size_t* _destlen, const char* src, size_t _srclen) { int ret; uLongf destlen = *_destlen; uLongf srclen = _srclen; ret = uncompress((Bytef *)dest, &destlen, (const Bytef *)src, srclen); if (ret != Z_OK){ cl_log(LOG_ERR, "%s: decompression failed", __FUNCTION__); return HA_FAIL; } *_destlen = destlen; return HA_OK; } static const char* zlib_getname(void) { return "zlib"; } static struct hb_compress_fns zlibOps ={ zlib_compress, zlib_decompress, zlib_getname, }; Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/Makefile.am0000644000000000000000000000377112120057602026346 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in if UPSTART SUBDIRS = dbus endif LRM_DIR = lrm INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl if UPSTART INCLUDES += $(DBUS_CFLAGS) endif halibdir = $(libdir)/@HB_PKG@ havarlibdir = $(localstatedir)/lib/@HB_PKG@ COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/lrm/liblrm.la \ $(GLIBLIB) plugindir = $(halibdir)/plugins/RAExec plugin_LTLIBRARIES = lsb.la ocf.la heartbeat.la if UPSTART plugin_LTLIBRARIES += upstart.la endif lsb_la_SOURCES = raexeclsb.c lsb_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version ocf_la_SOURCES = raexecocf.c ocf_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version heartbeat_la_SOURCES = raexechb.c heartbeat_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version if UPSTART upstart_la_SOURCES = raexecupstart.c upstart-dbus.c upstart_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version \ $(DBUS_LIBS) endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/dbus/Makefile.am0000644000000000000000000000046612120057602027301 0ustar00usergroup00000000000000if UPSTART BINDINGS=Upstart_Instance.h \ Upstart_Job.h \ Upstart.h all-local: for header in $(BINDINGS); do \ input=com.ubuntu.`echo $$header | sed 's/\.h//' | tr _ .`.xml; \ $(DBUS_BINDING_TOOL) --mode=glib-client $$input > $$header; \ done clean-local: rm -f $(BINDINGS) EXTRA_DIST = *.xml endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml0000644000000000000000000000321112120057602033261 0ustar00usergroup00000000000000 Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml0000644000000000000000000000520312120057602032232 0ustar00usergroup00000000000000 Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml0000644000000000000000000000354012120057602031523 0ustar00usergroup00000000000000 Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/raexechb.c0000644000000000000000000003101312120057602026225 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: raexechb.c * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This code implements the Resource Agent Plugin Module for LSB style. * It's a part of Local Resource Manager. Currently it's used by lrmd only. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PIL_PLUGINTYPE RA_EXEC_TYPE #define PIL_PLUGIN heartbeat #define PIL_PLUGINTYPE_S "RAExec" #define PIL_PLUGIN_S "heartbeat" #define PIL_PLUGINLICENSE LICENSE_PUBDOM #define PIL_PLUGINLICENSEURL URL_PUBDOM static const char * RA_PATH = HB_RA_DIR; static const char meta_data_template[] = "\n" "\n" "\n" "1.0\n" "\n" "%s" "\n" "%s\n" "\n" "\n" "\n" "This argument will be passed as the first argument to the " "heartbeat resource agent (assuming it supports one)\n" "\n" "argv[1]\n" "\n" "\n" "\n" "\n" "This argument will be passed as the second argument to the " "heartbeat resource agent (assuming it supports one)\n" "\n" "argv[2]\n" "\n" "\n" "\n" "\n" "This argument will be passed as the third argument to the " "heartbeat resource agent (assuming it supports one)\n" "\n" "argv[3]\n" "\n" "\n" "\n" "\n" "This argument will be passed as the fourth argument to the " "heartbeat resource agent (assuming it supports one)\n" "\n" "argv[4]\n" "\n" "\n" "\n" "\n" "This argument will be passed as the fifth argument to the " "heartbeat resource agent (assuming it supports one)\n" "\n" "argv[5]\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n"; /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra , const char * op_type, const char * std_output); static int get_resource_list(GList ** rsc_info); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_provider_list(const char* ra_type, GList ** providers); /* The end of exported function list */ /* The begin of internal used function & data list */ #define MAX_PARAMETER_NUM 40 typedef char * RA_ARGV[MAX_PARAMETER_NUM]; static const int MAX_LENGTH_OF_RSCNAME = 40, MAX_LENGTH_OF_OPNAME = 40; static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params, RA_ARGV params_argv); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static void* OurImports; static void* interfprivate; static int idebuglevel = 0; /* * Our plugin initialization and registration function * It gets called when the plugin gets loaded. */ PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); if (getenv(HADEBUGVAL) != NULL && atoi(getenv(HADEBUGVAL)) > 0 ) { idebuglevel = atoi(getenv(HADEBUGVAL)); cl_log(LOG_DEBUG, "LRM debug level set to %d", idebuglevel); } /* Register our interfaces */ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &raops, NULL, &OurInterface, &OurImports, interfprivate); } /* * Real work starts here ;-) */ static int execra( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params) { RA_ARGV params_argv; char ra_pathname[RA_MAX_NAME_LENGTH]; uniform_ret_execra_t exit_value; GString * debug_info; char * optype_tmp = NULL; int index_tmp = 0; /* How to generate the meta-data? There is nearly no value * information in meta-data build up in current way. * Should directly add meta-data to the script itself? */ if ( 0 == STRNCMP_CONST(op_type, "meta-data") ) { printf("%s", get_resource_meta(rsc_type, provider)); exit(0); } /* To simulate the 'monitor' operation with 'status'. * Now suppose there is no 'monitor' operation for heartbeat scripts. */ if ( 0 == STRNCMP_CONST(op_type, "monitor") ) { optype_tmp = g_strdup("status"); } else { optype_tmp = g_strdup(op_type); } /* Prepare the call parameter */ if (0 > prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)) { cl_log(LOG_ERR, "HB RA: Error of preparing parameters"); g_free(optype_tmp); return -1; } g_free(optype_tmp); get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname); /* let this log show only high loglevel. */ if (idebuglevel > 1) { debug_info = g_string_new(""); do { g_string_append(debug_info, params_argv[index_tmp]); g_string_append(debug_info, " "); } while (params_argv[++index_tmp] != NULL); debug_info->str[debug_info->len-1] = '\0'; cl_log(LOG_DEBUG, "RA instance %s executing: heartbeat::%s" , rsc_id, debug_info->str); g_string_free(debug_info, TRUE); } closefiles(); /* don't leak open files */ execv(ra_pathname, params_argv); cl_perror("(%s:%s:%d) execv failed for %s" , __FILE__, __FUNCTION__, __LINE__, ra_pathname); switch (errno) { case ENOENT: /* No such file or directory */ case EISDIR: /* Is a directory */ exit_value = EXECRA_NOT_INSTALLED; break; default: exit_value = EXECRA_EXEC_UNKNOWN_ERROR; } exit(exit_value); } static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params_ht, RA_ARGV params_argv) { int tmp_len, index; int ht_size = 0; int param_num = 0; char buf_tmp[20]; void * value_tmp; if (params_ht) { ht_size = g_hash_table_size(params_ht); } if ( ht_size+3 > MAX_PARAMETER_NUM ) { cl_log(LOG_ERR, "Too many parameters"); return -1; } /* Now suppose the parameter format stored in Hashtabe is as like as * key="1", value="-Wl,soname=test" * Moreover, the key is supposed as a string transfered from an integer. * It may be changed in the future. */ /* Notice: if ht_size==0, no actual arguments except op_type */ for (index = 1; index <= ht_size; index++ ) { snprintf(buf_tmp, sizeof(buf_tmp), "%d", index); value_tmp = g_hash_table_lookup(params_ht, buf_tmp); /* suppose the key is consecutive */ if ( value_tmp == NULL ) { /* cl_log(LOG_WARNING, "Parameter ordering error in"\ "prepare_cmd_parameters, raexeclsb.c"); cl_log(LOG_WARNING, "search key=%s.", buf_tmp); */ continue; } param_num ++; params_argv[param_num] = g_strdup((char *)value_tmp); } tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME); params_argv[0] = g_strndup(rsc_type, tmp_len); /* Add operation code as the last argument */ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME); params_argv[param_num+1] = g_strndup(op_type, tmp_len); /* Add the teminating NULL pointer */ params_argv[param_num+2] = NULL; return 0; } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output) { /* Now there is no formal related specification for Heartbeat RA * scripts. Temporarily deal as LSB init script. */ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible with LSB standard. */ const char * stop_pattern1 = "*stopped*", * stop_pattern2 = "*not*running*", * running_pattern1 = "*running*", * running_pattern2 = "*OK*"; char * lower_std_output = NULL; if(ret_execra == EXECRA_NOT_INSTALLED) { return ret_execra; } if ( 0 == STRNCMP_CONST(op_type, "status") || 0 == STRNCMP_CONST(op_type, "monitor")) { if (std_output == NULL ) { cl_log(LOG_WARNING, "No status output from the (hb) resource agent."); return EXECRA_NOT_RUNNING; } if (idebuglevel) { cl_log(LOG_DEBUG, "RA output was: [%s]", std_output); } lower_std_output = g_ascii_strdown(std_output, -1); if ( TRUE == g_pattern_match_simple(stop_pattern1 , lower_std_output) || TRUE == g_pattern_match_simple(stop_pattern2 , lower_std_output) ) { if (idebuglevel) { cl_log(LOG_DEBUG , "RA output [%s] matched stopped pattern" " [%s] or [%s]" , std_output , stop_pattern1 , stop_pattern2); } ret_execra = EXECRA_NOT_RUNNING; /* stopped */ } else if ( TRUE == g_pattern_match_simple(running_pattern1 , lower_std_output) || TRUE == g_pattern_match_simple(running_pattern2 , std_output) ) { if (idebuglevel) { cl_log(LOG_DEBUG , "RA output [%s] matched running" " pattern [%s] or [%s]" , std_output, running_pattern1 , running_pattern2); } ret_execra = EXECRA_OK; /* running */ } else { /* It didn't say it was running - must be stopped */ cl_log(LOG_DEBUG, "RA output [%s] didn't match any pattern" , std_output); ret_execra = EXECRA_NOT_RUNNING; /* stopped */ } g_free(lower_std_output); } /* For non-status operation return code */ if (ret_execra < 0) { ret_execra = EXECRA_UNKNOWN_ERROR; } return ret_execra; } static int get_resource_list(GList ** rsc_info) { return get_runnable_list(RA_PATH, rsc_info); } static char* get_resource_meta(const char* rsc_type, const char* provider) { GString * meta_data; meta_data = g_string_new(""); g_string_sprintf( meta_data, meta_data_template, rsc_type , rsc_type, rsc_type); return meta_data->str; } static int get_provider_list(const char* ra_type, GList ** providers) { if ( providers == NULL ) { cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL" , __FUNCTION__, __LINE__); return -2; } if ( *providers != NULL ) { cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL." "This will cause memory leak." , __FUNCTION__, __LINE__); } /* Now temporarily make it fixed */ *providers = g_list_append(*providers, g_strdup("heartbeat")); return g_list_length(*providers); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/raexeclsb.c0000644000000000000000000004225312120057602026424 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: raexeclsb.c * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This code implements the Resource Agent Plugin Module for LSB style. * It's a part of Local Resource Manager. Currently it's used by lrmd only. */ /* * Todo * 1) Use flex&bison to make the analysis functions for lsb compliant comment? * 2) Support multiple paths which contain lsb compliant RAs. * 3) Optional and additional actions analysis? */ #include #include #include #include #include #include #include #include #include #include #include /* Add it for compiling on OSX */ #include #include #include #include #include #include #define PIL_PLUGINTYPE RA_EXEC_TYPE #define PIL_PLUGIN lsb #define PIL_PLUGINTYPE_S "RAExec" #define PIL_PLUGIN_S "lsb" #define PIL_PLUGINLICENSE LICENSE_PUBDOM #define PIL_PLUGINLICENSEURL URL_PUBDOM /* meta-data template for lsb scripts */ /* Note: As for optional actions -- extracted from lsb standard. * The reload and the try-restart options are optional. Other init script * actions may be defined by the init script. */ #define meta_data_template \ "\n"\ "\n"\ "\n"\ " 1.0\n"\ " \n"\ " %s"\ " \n"\ " %s\n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " %s\n"\ " %s\n"\ " %s\n"\ " %s\n"\ " %s\n"\ " %s\n"\ " %s\n"\ " \n"\ "\n" /* The keywords for lsb-compliant comment */ #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO" #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO" #define PROVIDES "# Provides:" #define REQ_START "# Required-Start:" #define REQ_STOP "# Required-Stop:" #define SHLD_START "# Should-Start:" #define SHLD_STOP "# Should-Stop:" #define DFLT_START "# Default-Start:" #define DFLT_STOP "# Default-Stop:" #define SHORT_DSCR "# Short-Description:" #define DESCRIPTION "# Description:" #define ZAPXMLOBJ(m) \ if ( (m) != NULL ) { \ xmlFree(m); \ (m) = NULL; \ } #define RALSB_GET_VALUE(ptr, keyword) \ if ( (ptr == NULL) & (0 == strncasecmp(buffer, keyword, strlen(keyword))) ) { \ (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL,BAD_CAST buffer+strlen(keyword)); \ continue; \ } /* * Are there multiple paths? Now according to LSB init scripts, the answer * is 'no', but should be 'yes' for lsb none-init scripts? */ static const char * RA_PATH = LSB_RA_DIR; /* Map to the return code of the 'monitor' operation defined in the OCF RA * specification. */ static const int status_op_exitcode_map[] = { EXECRA_OK, /* LSB_STATUS_OK */ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_PID */ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_LOCK */ EXECRA_NOT_RUNNING, /* LSB_STATUS_STOPPED */ EXECRA_UNKNOWN_ERROR /* LSB_STATUS_UNKNOWN */ }; /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra , const char * op_type, const char * std_output); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_resource_list(GList ** rsc_info); static int get_provider_list(const char* ra_type, GList ** providers); /* The end of exported function list */ /* The begin of internal used function & data list */ #define MAX_PARAMETER_NUM 40 const int MAX_LENGTH_OF_RSCNAME = 40, MAX_LENGTH_OF_OPNAME = 40; typedef char * RA_ARGV[MAX_PARAMETER_NUM]; static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params, RA_ARGV params_argv); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static void* OurImports; static void* interfprivate; /* * Our plugin initialization and registration function * It gets called when the plugin gets loaded. */ PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interfaces */ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &raops, NULL, &OurInterface, &OurImports, interfprivate); } /* * Real work starts here ;-) */ static int execra( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params) { RA_ARGV params_argv; char ra_pathname[RA_MAX_NAME_LENGTH]; GString * debug_info; char * inherit_debuglevel = NULL; char * optype_tmp = NULL; int index_tmp = 0; int save_errno; /* Specially handle the operation "metameta-data". To build up its * output from templet, dummy data and its comment head. */ if ( 0 == STRNCMP_CONST(op_type, "meta-data")) { printf("%s", get_resource_meta(rsc_type, provider)); exit(0); } /* To simulate the 'monitor' operation with 'status'. * Now suppose there is no 'monitor' operation for LSB scripts. */ if (0 == STRNCMP_CONST(op_type, "monitor")) { optype_tmp = g_strdup("status"); } else { optype_tmp = g_strdup(op_type); } /* Prepare the call parameter */ if ( prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv) != 0) { cl_log(LOG_ERR, "lsb RA: Error of preparing parameters"); g_free(optype_tmp); return -1; } g_free(optype_tmp); get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname); /* let this log show only high loglevel. */ inherit_debuglevel = getenv(HADEBUGVAL); if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) { debug_info = g_string_new(""); do { g_string_append(debug_info, params_argv[index_tmp]); g_string_append(debug_info, " "); } while (params_argv[++index_tmp] != NULL); debug_info->str[debug_info->len-1] = '\0'; cl_log(LOG_DEBUG, "RA instance %s executing: lsb::%s" , rsc_id, debug_info->str); g_string_free(debug_info, TRUE); } closefiles(); /* don't leak open files */ execv(ra_pathname, params_argv); /* oops, exec failed */ save_errno = errno; /* cl_perror may change errno */ cl_perror("(%s:%s:%d) execv failed for %s" , __FILE__, __FUNCTION__, __LINE__, ra_pathname); errno = save_errno; exit(get_failed_exec_rc()); } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output) { /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible * with the LSB standard. */ if (ret_execra < 0) { return EXECRA_UNKNOWN_ERROR; } if(ret_execra == EXECRA_NOT_INSTALLED) { return ret_execra; } if ( 0 == STRNCMP_CONST(op_type, "status") || 0 == STRNCMP_CONST(op_type, "monitor")) { if (ret_execra < DIMOF(status_op_exitcode_map)) { ret_execra = status_op_exitcode_map[ret_execra]; } } return ret_execra; } static int get_resource_list(GList ** rsc_info) { char ra_pathname[RA_MAX_NAME_LENGTH]; FILE * fp; gboolean next_continue, found_begin_tag, is_lsb_script; int rc = 0; GList *cur, *tmp; const size_t BUFLEN = 80; char buffer[BUFLEN]; if ((rc = get_runnable_list(RA_PATH, rsc_info)) <= 0) { return rc; } /* Use the following comment line as the filter patterns to choose * the real LSB-compliant scripts. * "### BEGIN INIT INFO" and "### END INIT INFO" */ cur = g_list_first(*rsc_info); while ( cur != NULL ) { get_ra_pathname(RA_PATH, cur->data, NULL, ra_pathname); if ( (fp = fopen(ra_pathname, "r")) == NULL ) { tmp = g_list_next(cur); *rsc_info = g_list_remove(*rsc_info, cur->data); if (cur->data) g_free(cur->data); cur = tmp; continue; } is_lsb_script = FALSE; next_continue = FALSE; found_begin_tag = FALSE; while (NULL != fgets(buffer, BUFLEN, fp)) { /* Handle the lines over BUFLEN(80) columns, only * the first part is compared. */ if ( next_continue == TRUE ) { continue; } if (strlen(buffer) == BUFLEN ) { next_continue = TRUE; } else { next_continue = FALSE; } /* Shorten the search time */ if (buffer[0] != '#' && buffer[0] != ' ' && buffer[0] != '\n') { break; /* donnot find */ } if (found_begin_tag == TRUE && 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOEND_TAG , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) { is_lsb_script = TRUE; break; } if (found_begin_tag == FALSE && 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOBEGIN_TAG , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) { found_begin_tag = TRUE; } } fclose(fp); tmp = g_list_next(cur); /* * Temporarily remove the filter to the initscript, or many initscripts on * many distros, such as RHEL4 and fedora5, cannot be used by management GUI. * Please refer to the bug * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250 */ #if 0 if ( is_lsb_script != TRUE ) { *rsc_info = g_list_remove(*rsc_info, cur->data); g_free(cur->data); } #else (void) is_lsb_script; #endif cur = tmp; } return g_list_length(*rsc_info); } static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params_ht, RA_ARGV params_argv) { int tmp_len; int ht_size = 0; #if 0 /* Reserve it for possible furture use */ int index; void * value_tmp = NULL; char buf_tmp[20]; #endif if (params_ht) { ht_size = g_hash_table_size(params_ht); } /* Need 3 additonal spaces for accomodating: * argv[0] = RA_file_name(RA_TYPE) * argv[1] = operation * a terminal NULL */ if ( ht_size+3 > MAX_PARAMETER_NUM ) { cl_log(LOG_ERR, "Too many parameters"); return -1; } tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME); params_argv[0] = g_strndup(rsc_type, tmp_len); /* Add operation code as the first argument */ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME); params_argv[1] = g_strndup(op_type, tmp_len); /* * No actual arguments needed except op_type. * Add the teminating NULL pointer. */ params_argv[2] = NULL; if ( (ht_size != 0) && (0 != STRNCMP_CONST(op_type, "status")) ) { cl_log(LOG_WARNING, "For LSB init script, no additional " "parameters are needed."); } /* Actually comment the following code, but I still think it may be used * in the future for LSB none-initial scripts, so reserver it. */ #if 0 /* Now suppose the parameter formate stored in Hashtabe is like * key="1", value="-Wl,soname=test" * Moreover, the key is supposed as a string transfered from an integer. * It may be changed in the future. */ for (index = 1; index <= ht_size; index++ ) { snprintf(buf_tmp, sizeof(buf_tmp), "%d", index); value_tmp = g_hash_table_lookup(params_ht, buf_tmp); /* suppose the key is consecutive */ if ( value_tmp == NULL ) { cl_log(LOG_ERR, "Parameter ordering error in"\ "prepare_cmd_parameters, raexeclsb.c"); return -1; } params_argv[index+1] = g_strdup((char *)value_tmp); } #endif return 0; } static char* get_resource_meta(const char* rsc_type, const char* provider) { char ra_pathname[RA_MAX_NAME_LENGTH]; FILE * fp; gboolean next_continue; GString * meta_data; const size_t BUFLEN = 132; char buffer[BUFLEN]; char * provides = NULL, * req_start = NULL, * req_stop = NULL, * shld_start = NULL, * shld_stop = NULL, * dflt_start = NULL, * dflt_stop = NULL, * s_dscrpt = NULL, * xml_l_dscrpt = NULL; GString * l_dscrpt = NULL; /* * Use the following tags to find the LSb-compliant comment block. * "### BEGIN INIT INFO" and "### END INIT INFO" */ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname); if ( (fp = fopen(ra_pathname, "r")) == NULL ) { cl_log(LOG_ERR, "Failed to open lsb RA %s. No meta-data gotten." , rsc_type); return NULL; } meta_data = g_string_new(""); next_continue = FALSE; /* * Is not stick to the rule that the description should be located in the * comment block between "### BEGIN INIT INFO" and "### END INIT INFO". * Please refer to the bug * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250 */ #if 0 while (NULL != fgets(buffer, BUFLEN, fp)) { /* Handle the lines over BUFLEN(80) columns, only * the first part is compared. */ if ( next_continue == TRUE ) { continue; } if (strlen(buffer) == BUFLEN ) { next_continue = TRUE; } else { next_continue = FALSE; } if ( 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOBEGIN_TAG , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) { break; } } #else (void) next_continue; #endif /* Enter into the lsb-compliant comment block */ while ( NULL != fgets(buffer, BUFLEN, fp) ) { /* Now suppose each of the following eight arguments contain * only one line */ RALSB_GET_VALUE(provides, PROVIDES) RALSB_GET_VALUE(req_start, REQ_START) RALSB_GET_VALUE(req_stop, REQ_STOP) RALSB_GET_VALUE(shld_start, SHLD_START) RALSB_GET_VALUE(shld_stop, SHLD_STOP) RALSB_GET_VALUE(dflt_start, DFLT_START) RALSB_GET_VALUE(dflt_stop, DFLT_STOP) RALSB_GET_VALUE(s_dscrpt, SHORT_DSCR) /* Long description may cross multiple lines */ if ( (l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION , strlen(DESCRIPTION))) ) { l_dscrpt = g_string_new(buffer+strlen(DESCRIPTION)); /* Between # and keyword, more than one space, or a tab * character, indicates the continuation line. * Extracted from LSB init script standard */ while ( NULL != fgets(buffer, BUFLEN, fp) ) { if ( (0 == strncmp(buffer, "# ", 3)) || (0 == strncmp(buffer, "#\t", 2)) ) { buffer[0] = ' '; l_dscrpt = g_string_append(l_dscrpt , buffer); } else { fputs(buffer, fp); break; /* Long description ends */ } } continue; } if( l_dscrpt ) xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST (l_dscrpt->str)); if ( 0 == strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) { /* Get to the out border of LSB comment block */ break; } if ( buffer[0] != '#' ) { break; /* Out of comment block in the beginning */ } } fclose(fp); g_string_sprintf( meta_data, meta_data_template, rsc_type , (xml_l_dscrpt==NULL)? rsc_type : xml_l_dscrpt , (s_dscrpt==NULL)? rsc_type : s_dscrpt , (provides==NULL)? "" : provides , (req_start==NULL)? "" : req_start , (req_stop==NULL)? "" : req_stop , (shld_start==NULL)? "" : shld_start , (shld_stop==NULL)? "" : shld_stop , (dflt_start==NULL)? "" : dflt_start , (dflt_stop==NULL)? "" : dflt_stop ); ZAPXMLOBJ(xml_l_dscrpt); ZAPXMLOBJ(s_dscrpt); ZAPXMLOBJ(provides); ZAPXMLOBJ(req_start); ZAPXMLOBJ(req_stop); ZAPXMLOBJ(shld_start); ZAPXMLOBJ(shld_stop); ZAPXMLOBJ(dflt_start); ZAPXMLOBJ(dflt_stop); if( l_dscrpt ) g_string_free(l_dscrpt, TRUE); return meta_data->str; } static int get_provider_list(const char* ra_type, GList ** providers) { if ( providers == NULL ) { cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL" , __FUNCTION__, __LINE__); return -2; } if ( *providers != NULL ) { cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL." "This will cause memory leak." , __FUNCTION__, __LINE__); } /* Now temporarily make it fixed */ *providers = g_list_append(*providers, g_strdup("heartbeat")); return g_list_length(*providers); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/raexecocf.c0000644000000000000000000003261012120057602026407 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: raexecocf.c * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This code implements the Resource Agent Plugin Module for LSB style. * It's a part of Local Resource Manager. Currently it's used by lrmd only. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Add it for compiling on OSX */ #ifdef HAVE_TIME_H #include #endif #include # define PIL_PLUGINTYPE RA_EXEC_TYPE # define PIL_PLUGINTYPE_S "RAExec" # define PIL_PLUGINLICENSE LICENSE_PUBDOM # define PIL_PLUGINLICENSEURL URL_PUBDOM # define PIL_PLUGIN ocf # define PIL_PLUGIN_S "ocf" /* * Are there multiple paths? Now according to OCF spec, the answer is 'no'. * But actually or for future? */ static const char * RA_PATH = OCF_RA_DIR; /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output); static int get_resource_list(GList ** rsc_info); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_provider_list(const char* ra_type, GList ** providers); /* The end of exported function list */ /* The begin of internal used function & data list */ static void add_OCF_prefix(GHashTable * params, GHashTable * new_params); static void add_OCF_env_vars(GHashTable * env, const char * rsc_id, const char * rsc_type, const char * provider); static void add_prefix_foreach(gpointer key, gpointer value, gpointer user_data); static void hash_to_str(GHashTable * , GString *); static void hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data); static int raexec_setenv(GHashTable * env_params); static void set_env(gpointer key, gpointer value, gpointer user_data); static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data); static int get_providers(const char* class_path, const char* op_type, GList ** providers); static void merge_string_list(GList** old, GList* new); static gint compare_str(gconstpointer a, gconstpointer b); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static void* OurImports; static void* interfprivate; /* * Our plugin initialization and registration function * It gets called when the plugin gets loaded. */ PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interfaces */ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &raops, NULL, &OurInterface, &OurImports, interfprivate); } /* * The function to execute a RA. */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params) { char ra_pathname[RA_MAX_NAME_LENGTH]; GHashTable * tmp_for_setenv; GString * params_gstring; char * inherit_debuglevel = NULL; int save_errno; get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname); /* Setup environment correctly */ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal); add_OCF_prefix(params, tmp_for_setenv); add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider); raexec_setenv(tmp_for_setenv); g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL); g_hash_table_destroy(tmp_for_setenv); /* let this log show only high loglevel. */ inherit_debuglevel = getenv(HADEBUGVAL); if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) { params_gstring = g_string_new(""); hash_to_str(params, params_gstring); cl_log(LOG_DEBUG, "RA instance %s executing: OCF::%s %s. Parameters: " "{%s}", rsc_id, rsc_type, op_type, params_gstring->str); g_string_free(params_gstring, TRUE); } closefiles(); /* don't leak open files */ /* execute the RA */ execl(ra_pathname, ra_pathname, op_type, (const char *)NULL); /* oops, exec failed */ save_errno = errno; /* cl_perror may change errno */ cl_perror("(%s:%s:%d) execl failed for %s" , __FILE__, __FUNCTION__, __LINE__, ra_pathname); errno = save_errno; exit(get_failed_exec_rc()); } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output) { /* Because the UNIFORM_RET_EXECRA is compatible with OCF standard, * no actual mapping except validating, which ensure the return code * will be in the range 0 to 7. Too strict? */ if (ret_execra < 0 || ret_execra > 9) { cl_log(LOG_WARNING, "mapped the invalid return code %d." , ret_execra); ret_execra = EXECRA_UNKNOWN_ERROR; } return ret_execra; } static gint compare_str(gconstpointer a, gconstpointer b) { return strncmp(a,b,RA_MAX_NAME_LENGTH); } static int get_resource_list(GList ** rsc_info) { struct dirent **namelist; GList* item; int file_num; char subdir[FILENAME_MAX+1]; if ( rsc_info == NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list"); return -2; } if ( *rsc_info != NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list."\ "will cause memory leak."); *rsc_info = NULL; } file_num = scandir(RA_PATH, &namelist, NULL, alphasort); if (file_num < 0) { return -2; } while (file_num--) { GList* ra_subdir = NULL; struct stat prop; if ('.' == namelist[file_num]->d_name[0]) { free(namelist[file_num]); continue; } snprintf(subdir,FILENAME_MAX,"%s/%s", RA_PATH, namelist[file_num]->d_name); if (stat(subdir, &prop) == -1) { cl_perror("%s:%s:%d: stat failed for %s" , __FILE__, __FUNCTION__, __LINE__, subdir); free(namelist[file_num]); continue; } else if (!S_ISDIR(prop.st_mode)) { free(namelist[file_num]); continue; } get_runnable_list(subdir,&ra_subdir); merge_string_list(rsc_info,ra_subdir); while (NULL != (item = g_list_first(ra_subdir))) { ra_subdir = g_list_remove_link(ra_subdir, item); g_free(item->data); g_list_free_1(item); } free(namelist[file_num]); } free(namelist); return 0; } static void merge_string_list(GList** old, GList* new) { GList* item = NULL; char* newitem; for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){ if (!g_list_find_custom(*old, item->data,compare_str)){ newitem = g_strndup(item->data,RA_MAX_NAME_LENGTH); *old = g_list_append(*old, newitem); } } } static int get_provider_list(const char* ra_type, GList ** providers) { int ret; ret = get_providers(RA_PATH, ra_type, providers); if (0>ret) { cl_log(LOG_ERR, "scandir failed in OCF RA plugin"); } return ret; } static char* get_resource_meta(const char* rsc_type, const char* provider) { const int BUFF_LEN=4096; int read_len = 0; char buff[BUFF_LEN]; char* data = NULL; GString* g_str_tmp = NULL; char ra_pathname[RA_MAX_NAME_LENGTH]; FILE* file = NULL; GHashTable * tmp_for_setenv; struct timespec short_sleep = {0,200000000L}; /*20ms*/ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname); strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH-strlen(ra_pathname)-1); tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal); add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider); raexec_setenv(tmp_for_setenv); g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL); g_hash_table_destroy(tmp_for_setenv); file = popen(ra_pathname, "r"); if (NULL==file) { cl_log(LOG_ERR, "%s: popen failed: %s", __FUNCTION__, strerror(errno)); return NULL; } g_str_tmp = g_string_new(""); while(!feof(file)) { read_len = fread(buff, 1, BUFF_LEN - 1, file); if (0len) { g_string_free(g_str_tmp, TRUE); return NULL; } data = (char*)g_new(char, g_str_tmp->len+1); data[0] = data[g_str_tmp->len] = 0; strncpy(data, g_str_tmp->str, g_str_tmp->len); g_string_free(g_str_tmp, TRUE); return data; } static void add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params) { if (env_params) { g_hash_table_foreach(env_params, add_prefix_foreach, new_env_params); } } static void add_prefix_foreach(gpointer key, gpointer value, gpointer user_data) { const int MAX_LENGTH_OF_ENV = 128; int prefix = STRLEN_CONST("OCF_RESKEY_"); GHashTable * new_hashtable = (GHashTable *) user_data; char * newkey; int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix+1; newkey = g_new(gchar, keylen); strncpy(newkey, "OCF_RESKEY_", keylen); strncat(newkey, key, keylen-strlen(newkey)-1); g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value)); } static void hash_to_str(GHashTable * params , GString * str) { if (params) { g_hash_table_foreach(params, hash_to_str_foreach, str); } } static void hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data) { char buffer_tmp[60]; GString * str = (GString *)user_data; snprintf(buffer_tmp, 60, "%s=%s ", (char *)key, (char *)value); str = g_string_append(str, buffer_tmp); } static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data) { g_free(key); g_free(value); return TRUE; } static int raexec_setenv(GHashTable * env_params) { if (env_params) { g_hash_table_foreach(env_params, set_env, NULL); } return 0; } static void set_env(gpointer key, gpointer value, gpointer user_data) { if (setenv(key, value, 1) != 0) { cl_log(LOG_ERR, "setenv failed in raexecocf."); } } static int get_providers(const char* class_path, const char* ra_type, GList ** providers) { struct dirent **namelist; int file_num; if ( providers == NULL ) { cl_log(LOG_ERR, "Parameter error: get_providers"); return -2; } if ( *providers != NULL ) { cl_log(LOG_ERR, "Parameter error: get_providers."\ "will cause memory leak."); *providers = NULL; } file_num = scandir(class_path, &namelist, 0, alphasort); if (file_num < 0) { return -2; }else{ char tmp_buffer[FILENAME_MAX+1]; struct stat prop; while (file_num--) { if ('.' == namelist[file_num]->d_name[0]) { free(namelist[file_num]); continue; } snprintf(tmp_buffer,FILENAME_MAX,"%s/%s", class_path, namelist[file_num]->d_name); stat(tmp_buffer, &prop); if (!S_ISDIR(prop.st_mode)) { free(namelist[file_num]); continue; } snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s", class_path, namelist[file_num]->d_name, ra_type); if ( filtered(tmp_buffer) == TRUE ) { *providers = g_list_append(*providers, g_strdup(namelist[file_num]->d_name)); } free(namelist[file_num]); } free(namelist); } return g_list_length(*providers); } static void add_OCF_env_vars(GHashTable * env, const char * rsc_id, const char * rsc_type, const char * provider) { if ( env == NULL ) { cl_log(LOG_WARNING, "env should not be a NULL pointer."); return; } g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"), g_strdup("1")); g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"), g_strdup("0")); g_hash_table_insert(env, g_strdup("OCF_ROOT"), g_strdup(OCF_ROOT_DIR)); if ( rsc_id != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"), g_strdup(rsc_id)); } /* Currently the rsc_type=="the filename of the RA script/executable", * It seems always correct even in the furture. ;-) */ if ( rsc_type != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"), g_strdup(rsc_type)); } /* Notes: this is not added to specification yet. Sept 10,2004 */ if ( provider != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"), g_strdup(provider)); } } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/raexecupstart.c0000644000000000000000000001413012120057602027337 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: raexecupstart.c * Copyright (C) 2010 Senko Rasic * Copyright (c) 2010 Ante Karamatic * * Heavily based on raexeclsb.c and raexechb.c: * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This code implements the Resource Agent Plugin Module for Upstart. * It's a part of Local Resource Manager. Currently it's used by lrmd only. */ #define PIL_PLUGINTYPE RA_EXEC_TYPE #define PIL_PLUGIN upstart #define PIL_PLUGINTYPE_S "RAExec" #define PIL_PLUGIN_S "upstart" #define PIL_PLUGINLICENSE LICENSE_PUBDOM #define PIL_PLUGINLICENSEURL URL_PUBDOM #include #include #include #include #include #include #include #include #include #include #include /* Add it for compiling on OSX */ #include #include #include #include #include #include #include #include "upstart-dbus.h" #define meta_data_template \ "\n"\ "\n"\ "\n"\ " 1.0\n"\ " \n"\ " %s"\ " \n"\ " %s\n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ " \n"\ "\n" /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra , const char * op_type, const char * std_output); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_resource_list(GList ** rsc_info); static int get_provider_list(const char* ra_type, GList ** providers); /* The end of exported function list */ /* The begin of internal used function & data list */ #define MAX_PARAMETER_NUM 40 const int MAX_LENGTH_OF_RSCNAME = 40, MAX_LENGTH_OF_OPNAME = 40; typedef char * RA_ARGV[MAX_PARAMETER_NUM]; /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static void* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports) { PluginImports = imports; OurPlugin = us; imports->register_plugin(us, &OurPIExports); g_type_init (); return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &raops, NULL, &OurInterface, &OurImports, interfprivate); } static int execra( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params) { UpstartJobCommand cmd; if (!g_strcmp0(op_type, "meta-data")) { printf("%s", get_resource_meta(rsc_type, provider)); exit(EXECRA_OK); } else if (!g_strcmp0(op_type, "monitor") || !g_strcmp0(op_type, "status")) { gboolean running = upstart_job_is_running (rsc_type); printf("%s", running ? "running" : "stopped"); if (running) exit(EXECRA_OK); else exit(EXECRA_NOT_RUNNING); } else if (!g_strcmp0(op_type, "start")) { cmd = UPSTART_JOB_START; } else if (!g_strcmp0(op_type, "stop")) { cmd = UPSTART_JOB_STOP; } else if (!g_strcmp0(op_type, "restart")) { cmd = UPSTART_JOB_RESTART; } else { exit(EXECRA_UNIMPLEMENT_FEATURE); } /* It'd be better if it returned GError, so we can distinguish * between failure modes (can't contact upstart, no such job, * or failure to do action. */ if (upstart_job_do(rsc_type, cmd, timeout)) { exit(EXECRA_OK); } else { exit(EXECRA_NO_RA); } } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output) { /* no need to map anything, execra() returns correct exit code */ return ret_execra; } static int get_resource_list(GList ** rsc_info) { gchar **jobs; gint i; *rsc_info = NULL; jobs = upstart_get_all_jobs(); if (!jobs) return 0; for (i = 0; jobs[i] != NULL; i++) { *rsc_info = g_list_prepend(*rsc_info, jobs[i]); } /* free the array, but not the strings */ g_free(jobs); *rsc_info = g_list_reverse(*rsc_info); return g_list_length(*rsc_info); } static char * get_resource_meta (const gchar *rsc_type, const gchar *provider) { return g_strdup_printf(meta_data_template, rsc_type, rsc_type, rsc_type); } static int get_provider_list (const gchar *ra_type, GList **providers) { *providers = g_list_prepend(*providers, g_strdup("upstart")); return g_list_length(*providers); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/upstart-dbus.c0000644000000000000000000002130412120057602027103 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: upstart-dbus.c * Copyright (C) 2010 Senko Rasic * Copyright (c) 2010 Ante Karamatic * * * Each exported function is standalone, and creates a new connection to * the upstart daemon. This is because lrmd plugins fork off for exec, * and if we try and share the connection, the whole thing blocks * indefinitely. */ #include "upstart-dbus.h" #include #include #include #include "dbus/Upstart.h" #include "dbus/Upstart_Job.h" #include "dbus/Upstart_Instance.h" #include #define SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" #define UPSTART_BUS_ADDRESS "unix:abstract=/com/ubuntu/upstart" #define UPSTART_SERVICE_NAME "com.ubuntu.Upstart" #define UPSTART_MANAGER_PATH "/com/ubuntu/Upstart" #define UPSTART_IFACE "com.ubuntu.Upstart0_6" #define UPSTART_JOB_IFACE UPSTART_IFACE ".Job" #define UPSTART_INSTANCE_IFACE UPSTART_IFACE ".Instance" #define UPSTART_ERROR_ALREADY_STARTED UPSTART_IFACE ".Error.AlreadyStarted" #define UPSTART_ERROR_UNKNOWN_INSTANCE UPSTART_IFACE ".Error.UnknownInstance" static DBusGConnection * get_connection(void) { GError *error = NULL; DBusGConnection *conn; conn = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, NULL, &error); if (error) { g_error_free(error); error = NULL; conn = dbus_g_connection_open("unix:abstract=/com/ubuntu/upstart", &error); if (error) { g_warning("Can't connect to either system or Upstart " "DBus bus."); g_error_free(error); return NULL; } } return conn; } static DBusGProxy * new_proxy(DBusGConnection *conn, const gchar *object_path, const gchar *iface) { return dbus_g_proxy_new_for_name(conn, UPSTART_SERVICE_NAME, object_path, iface); } static GHashTable * get_object_properties(DBusGProxy *obj, const gchar *iface) { GError *error = NULL; DBusGProxy *proxy; GHashTable *asv; GHashTable *retval; GHashTableIter iter; gpointer k, v; proxy = dbus_g_proxy_new_from_proxy(obj, DBUS_INTERFACE_PROPERTIES, NULL); dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING, iface, G_TYPE_INVALID, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &asv, G_TYPE_INVALID); if (error) { g_warning("Error getting %s properties: %s", iface, error->message); g_error_free(error); g_object_unref(proxy); return NULL; } retval = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_iter_init(&iter, asv); while (g_hash_table_iter_next(&iter, &k, &v)) { gchar *key = k; GValue *val = v; /* all known properties are strings */ if (G_VALUE_TYPE(val) == G_TYPE_STRING) { g_hash_table_insert(retval, g_strdup(key), g_value_dup_string(val)); } } g_hash_table_destroy(asv); return retval; } gchar ** upstart_get_all_jobs(void) { DBusGConnection *conn; DBusGProxy *manager; GError *error = NULL; GPtrArray *array; gchar **retval = NULL; gint i, j; conn = get_connection(); if (!conn) return NULL; manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE); dbus_g_proxy_call(manager, "GetAllJobs", &error, G_TYPE_INVALID, dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &array, G_TYPE_INVALID); if (error) { g_warning("Can't call GetAllJobs: %s", error->message); g_error_free(error); g_object_unref(manager); dbus_g_connection_unref(conn); return NULL; } retval = g_new0(gchar *, array->len + 1); for (i = 0, j = 0; i < array->len; i++) { DBusGProxy *job; job = new_proxy(conn, g_ptr_array_index(array, i), UPSTART_JOB_IFACE); if (job) { GHashTable *props = get_object_properties(job, UPSTART_JOB_IFACE); if (props) { gchar *name = g_hash_table_lookup(props, "name"); if (name) retval[j++] = g_strdup(name); g_hash_table_destroy(props); } g_object_unref(job); } } g_ptr_array_free(array, TRUE); g_object_unref(manager); dbus_g_connection_unref(conn); return retval; } static DBusGProxy * upstart_get_job_by_name(DBusGConnection *conn, DBusGProxy *manager, const gchar *name) { GError *error = NULL; gchar *object_path; DBusGProxy *retval; dbus_g_proxy_call(manager, "GetJobByName", &error, G_TYPE_STRING, name, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &object_path, G_TYPE_INVALID); if (error) { g_warning("Error calling GetJobByName: %s", error->message); g_error_free(error); return NULL; } retval = new_proxy(conn, object_path, UPSTART_JOB_IFACE); g_free(object_path); return retval; } static gchar ** get_job_instances(DBusGProxy *job) { GError *error = NULL; GPtrArray *array; gchar **retval; gint i; dbus_g_proxy_call(job, "GetAllInstances", &error, G_TYPE_INVALID, dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &array, G_TYPE_INVALID); if (error) { g_warning("Can't call GetAllInstances: %s", error->message); g_error_free(error); return NULL; } retval = g_new0(gchar *, array->len + 1); for (i = 0; i < array->len; i++) { retval[i] = g_ptr_array_index(array, i); } g_ptr_array_free(array, TRUE); return retval; } static DBusGProxy * get_first_instance(DBusGConnection *conn, DBusGProxy *job) { gchar **instances; DBusGProxy *instance = NULL; instances = get_job_instances(job); if (!instances) return NULL; if (*instances) { instance = new_proxy(conn, instances[0], UPSTART_INSTANCE_IFACE); } g_strfreev(instances); return instance; } gboolean upstart_job_is_running(const gchar *name) { DBusGConnection *conn; DBusGProxy *manager; DBusGProxy *job; gboolean retval = FALSE; conn = get_connection(); if (!conn) return FALSE; manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE); job = upstart_get_job_by_name(conn, manager, name); if (job) { DBusGProxy *instance = get_first_instance(conn, job); if (instance) { GHashTable *props = get_object_properties(instance, UPSTART_INSTANCE_IFACE); if (props) { const gchar *state = g_hash_table_lookup(props, "state"); retval = !g_strcmp0(state, "running"); g_hash_table_destroy(props); } g_object_unref(instance); } g_object_unref(job); } g_object_unref(manager); dbus_g_connection_unref(conn); return retval; } gboolean upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout) { DBusGConnection *conn; DBusGProxy *manager; DBusGProxy *job; gboolean retval; conn = get_connection(); if (!conn) return FALSE; manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE); job = upstart_get_job_by_name(conn, manager, name); if (job) { GError *error = NULL; const gchar *cmd_name = NULL; gchar *instance_path = NULL; gchar *no_args[] = { NULL }; switch (cmd) { case UPSTART_JOB_START: cmd_name = "Start"; dbus_g_proxy_call_with_timeout (job, cmd_name, timeout, &error, G_TYPE_STRV, no_args, G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &instance_path, G_TYPE_INVALID); g_free (instance_path); break; case UPSTART_JOB_STOP: cmd_name = "Stop"; dbus_g_proxy_call_with_timeout(job, cmd_name, timeout, &error, G_TYPE_STRV, no_args, G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID, G_TYPE_INVALID); break; case UPSTART_JOB_RESTART: cmd_name = "Restart"; dbus_g_proxy_call_with_timeout (job, cmd_name, timeout, &error, G_TYPE_STRV, no_args, G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &instance_path, G_TYPE_INVALID); g_free (instance_path); break; default: g_assert_not_reached(); } if (error) { g_warning("Could not issue %s: %s", cmd_name, error->message); /* ignore "already started" or "not running" errors */ if (dbus_g_error_has_name(error, UPSTART_ERROR_ALREADY_STARTED) || dbus_g_error_has_name(error, UPSTART_ERROR_UNKNOWN_INSTANCE)) { retval = TRUE; } else { retval = FALSE; } g_error_free(error); } else { retval = TRUE; } g_object_unref(job); } else { retval = FALSE; } g_object_unref(manager); dbus_g_connection_unref(conn); return retval; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/lrm/upstart-dbus.h0000644000000000000000000000241012120057602027105 0ustar00usergroup00000000000000/* * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * File: upstart-dbus.c * Copyright (C) 2010 Senko Rasic * Copyright (c) 2010 Ante Karamatic */ #ifndef _UPSTART_DBUS_H_ #define _UPSTART_DBUS_H_ #include typedef enum { UPSTART_JOB_START, UPSTART_JOB_STOP, UPSTART_JOB_RESTART } UpstartJobCommand; G_GNUC_INTERNAL gchar **upstart_get_all_jobs(void); G_GNUC_INTERNAL gboolean upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout); G_GNUC_INTERNAL gboolean upstart_job_is_running (const gchar *name); #endif /* _UPSTART_DBUS_H_ */ Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/Makefile.am0000644000000000000000000001637212120057602027245 0ustar00usergroup00000000000000# # stonith: STONITH plugins for Linux-HA # # Copyright (C) 2001 Alan Robertson # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = external INCFILES = stonith_expect_helpers.h \ stonith_plugin_common.h \ stonith_signal.h \ stonith_config_xml.h idir=$(includedir)/stonith i_HEADERS = $(INCFILES) INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl AM_CFLAGS = @NON_FATAL_CFLAGS@ # # We need "vacmclient_api.h" and -lvacmclient to make the VACM # plugin work. # if USE_VACM vacm_LIB = vacm.la else vacm_LIB = endif # # We need , , # , , -lsnmp and -lcrypto # for the apcmastersnmp plugin to work # if USE_APC_SNMP apcmastersnmp_LIB = apcmastersnmp.la wti_mpc.la else apcmastersnmp_LIB = endif if IPMILAN_BUILD OPENIPMI_LIB = -lOpenIPMI -lOpenIPMIposix -lOpenIPMIutils libipmilan_LIB = libipmilan.la ipmilan_LIB = ipmilan.la ipmilan_TEST = ipmilantest else OPENIPMI_LIB = libipmilan_LIB = ipmilan_LIB = endif # # We need , , # , , # -lcurl and -lxml2 for the drac3 plugin to work # if USE_DRAC3 drac3_LIB = drac3.la else drac3_LIB = endif # # We need OpenHPI to make the IBM BladeCenter plugin work. # if USE_OPENHPI bladehpi_LIB = bladehpi.la else bladehpi_LIB = endif noinst_PROGRAMS = $(ipmilan_TEST) ipmilantest_SOURCES = ipmilan_test.c ipmilantest_LDADD = $(libipmilan_LIB) \ $(top_builddir)/lib/pils/libpils.la \ $(top_builddir)/lib/stonith/libstonith.la \ $(OPENIPMI_LIB) ## libraries plugindir = $(stonith_plugindir)/stonith2 plugin_LTLIBRARIES = apcmaster.la \ $(apcmastersnmp_LIB) \ apcsmart.la \ baytech.la \ $(bladehpi_LIB) \ cyclades.la \ $(drac3_LIB) \ external.la \ rhcs.la \ ibmhmc.la \ $(ipmilan_LIB) \ meatware.la \ null.la \ nw_rpc100s.la \ rcd_serial.la \ rps10.la \ ssh.la \ suicide.la \ $(vacm_LIB) \ wti_nps.la noinst_LTLIBRARIES = $(libipmilan_LIB) apcmaster_la_SOURCES = apcmaster.c $(INCFILES) apcmaster_la_LDFLAGS = -export-dynamic -module -avoid-version apcmaster_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) apcmastersnmp_la_SOURCES= apcmastersnmp.c $(INCFILES) apcmastersnmp_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \ @CRYPTOLIB@ apcmastersnmp_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) apcsmart_la_SOURCES = apcsmart.c $(INCFILES) apcsmart_la_LDFLAGS = -export-dynamic -module -avoid-version apcsmart_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) baytech_la_SOURCES = baytech.c $(INCFILES) baytech_la_LDFLAGS = -export-dynamic -module -avoid-version baytech_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) bladehpi_la_SOURCES = bladehpi.c $(INCFILES) bladehpi_la_LDFLAGS = -export-dynamic -module -avoid-version bladehpi_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) -lopenhpi cyclades_la_SOURCES = cyclades.c $(INCFILES) cyclades_la_LDFLAGS = -export-dynamic -module -avoid-version cyclades_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) drac3_la_SOURCES = drac3.c drac3_command.c drac3_command.h drac3_hash.c drac3_hash.h $(INCFILES) drac3_la_LDFLAGS = -export-dynamic -module -avoid-version drac3_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la -lcurl -lxml2 $(GLIBLIB) external_la_SOURCES = external.c $(INCFILES) external_la_LDFLAGS = -export-dynamic -module -avoid-version external_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la rhcs_la_SOURCES = rhcs.c $(INCFILES) rhcs_la_LDFLAGS = -export-dynamic -module -avoid-version rhcs_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la ibmhmc_la_SOURCES = ibmhmc.c $(INCFILES) ibmhmc_la_LDFLAGS = -export-dynamic -module -avoid-version ibmhmc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) ipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES) ipmilan_la_LDFLAGS = -export-dynamic -module -avoid-version ipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB) libipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES) libipmilan_la_LDFLAGS = -version-info 1:0:0 libipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB) meatware_la_SOURCES = meatware.c $(INCFILES) meatware_la_LDFLAGS = -export-dynamic -module -avoid-version meatware_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) null_la_SOURCES = null.c $(INCFILES) null_la_LDFLAGS = -export-dynamic -module -avoid-version null_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) nw_rpc100s_la_SOURCES = nw_rpc100s.c $(INCFILES) nw_rpc100s_la_LDFLAGS = -export-dynamic -module -avoid-version nw_rpc100s_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) rcd_serial_la_SOURCES = rcd_serial.c $(INCFILES) rcd_serial_la_LDFLAGS = -export-dynamic -module -avoid-version rcd_serial_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) rps10_la_SOURCES = rps10.c $(INCFILES) rps10_la_LDFLAGS = -export-dynamic -module -avoid-version rps10_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) ssh_la_SOURCES = ssh.c $(INCFILES) ssh_la_LDFLAGS = -export-dynamic -module -avoid-version ssh_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la vacm_la_SOURCES = vacm.c $(INCFILES) vacm_la_LDFLAGS = -export-dynamic -module -avoid-version vacm_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la wti_nps_la_SOURCES = wti_nps.c $(INCFILES) wti_nps_la_LDFLAGS = -export-dynamic -module -avoid-version wti_nps_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) wti_mpc_la_SOURCES= wti_mpc.c $(INCFILES) wti_mpc_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \ @CRYPTOLIB@ wti_mpc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) suicide_la_SOURCES = suicide.c $(INCFILES) suicide_la_LDFLAGS = -export-dynamic -module -avoid-version suicide_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la stonithscriptdir = $(stonith_plugindir)/stonith2 stonithscript_SCRIPTS = ribcl.py Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/apcmaster.c0000644000000000000000000004614012120057602027330 0ustar00usergroup00000000000000/* * * Copyright 2001 Mission Critical Linux, Inc. * * All Rights Reserved. */ /* * Stonith module for APC Master Switch (AP9211) * * Copyright (c) 2001 Mission Critical Linux, Inc. * author: mike ledoux * author: Todd Wheeling * mangled by Sun Jiang Dong, , IBM, 2005 * * Based strongly on original code from baytech.c by Alan Robertson. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Observations/Notes * * 1. The APC MasterSwitch, unlike the BayTech network power switch, * accepts only one (telnet) connection/session at a time. When one * session is active, any subsequent attempt to connect to the MasterSwitch * will result in a connection refused/closed failure. In a cluster * environment or other environment utilizing polling/monitoring of the * MasterSwitch (from multiple nodes), this can clearly cause problems. * Obviously the more nodes and the shorter the polling interval, the more * frequently such errors/collisions may occur. * * 2. We observed that on busy networks where there may be high occurances * of broadcasts, the MasterSwitch became unresponsive. In some * configurations this necessitated placing the power switch onto a * private subnet. */ #include #define DEVICE "APC MasterSwitch" #define DOESNT_USE_STONITHKILLCOMM 1 #include "stonith_plugin_common.h" #define PIL_PLUGIN apcmaster #define PIL_PLUGIN_S "apcmaster" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" static StonithPlugin * apcmaster_new(const char *); static void apcmaster_destroy(StonithPlugin *); static const char * const * apcmaster_get_confignames(StonithPlugin *); static int apcmaster_set_config(StonithPlugin *, StonithNVpair *); static const char * apcmaster_getinfo(StonithPlugin * s, int InfoType); static int apcmaster_status(StonithPlugin * ); static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host); static char ** apcmaster_hostlist(StonithPlugin *); static struct stonith_ops apcmasterOps ={ apcmaster_new, /* Create new STONITH object */ apcmaster_destroy, /* Destroy STONITH object */ apcmaster_getinfo, /* Return STONITH info string */ apcmaster_get_confignames, /* Get configuration parameters */ apcmaster_set_config, /* Set configuration */ apcmaster_status, /* Return STONITH device status */ apcmaster_reset_req, /* Request a reset */ apcmaster_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_expect_helpers.h" PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &apcmasterOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * I have an AP9211. This code has been tested with this switch. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; pid_t pid; int rdfd; int wrfd; char * device; char * user; char * passwd; }; static const char * pluginid = "APCMS-Stonith"; static const char * NOTpluginID = "APCMS device has been destroyed"; /* * Different expect strings that we get from the APC MasterSwitch */ #define APCMSSTR "American Power Conversion" static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0} , {NULL,0,0}}; static struct Etoken login[] = { {"User Name :", 0, 0}, {NULL,0,0}}; static struct Etoken password[] = { {"Password :", 0, 0} ,{NULL,0,0}}; static struct Etoken Prompt[] = { {"> ", 0, 0} ,{NULL,0,0}}; static struct Etoken LoginOK[] = { {APCMSSTR, 0, 0} , {"User Name :", 1, 0} ,{NULL,0,0}}; static struct Etoken Separator[] = { {"-----", 0, 0} ,{NULL,0,0}}; /* We may get a notice about rebooting, or a request for confirmation */ static struct Etoken Processing[] = { {"Press to continue", 0, 0} , {"Enter 'YES' to continue", 1, 0} , {NULL,0,0}}; #include "stonith_config_xml.h" static const char *apcmasterXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_LOGIN_PARM XML_PASSWD_PARM XML_PARAMETERS_END; static int MS_connect_device(struct pluginDevice * ms); static int MSLogin(struct pluginDevice * ms); static int MSRobustLogin(struct pluginDevice * ms); static int MSNametoOutlet(struct pluginDevice*, const char * name); static int MSReset(struct pluginDevice*, int outletNum, const char * host); static int MSLogout(struct pluginDevice * ms); #if defined(ST_POWERON) && defined(ST_POWEROFF) static int apcmaster_onoff(struct pluginDevice*, int outletnum, const char * unitid , int request); #endif /* Login to the APC Master Switch */ static int MSLogin(struct pluginDevice * ms) { EXPECT(ms->rdfd, EscapeChar, 10); /* * We should be looking at something like this: * User Name : */ EXPECT(ms->rdfd, login, 10); SEND(ms->wrfd, ms->user); SEND(ms->wrfd, "\r"); /* Expect "Password :" */ EXPECT(ms->rdfd, password, 10); SEND(ms->wrfd, ms->passwd); SEND(ms->wrfd, "\r"); switch (StonithLookFor(ms->rdfd, LoginOK, 30)) { case 0: /* Good! */ LOG(PIL_INFO, "Successful login to %s.", ms->idinfo); break; case 1: /* Uh-oh - bad password */ LOG(PIL_CRIT, "Invalid password for %s.", ms->idinfo); return(S_ACCESS); default: return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } return(S_OK); } /* Attempt to login up to 20 times... */ static int MSRobustLogin(struct pluginDevice * ms) { int rc = S_OOPS; int j = 0; for ( ; ; ) { if (MS_connect_device(ms) == S_OK) { rc = MSLogin(ms); if( rc == S_OK ) { break; } } if ((++j) == 20) { break; } else { sleep(1); } } return rc; } /* Log out of the APC Master Switch */ static int MSLogout(struct pluginDevice* ms) { int rc; /* Make sure we're in the right menu... */ /*SEND(ms->wrfd, "\033\033\033\033\033\033\033"); */ SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); /* Expect "> " */ rc = StonithLookFor(ms->rdfd, Prompt, 5); /* "4" is logout */ SEND(ms->wrfd, "4\r"); close(ms->wrfd); close(ms->rdfd); ms->wrfd = ms->rdfd = -1; return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS)); } /* Reset (power-cycle) the given outlets */ static int MSReset(struct pluginDevice* ms, int outletNum, const char *host) { char unum[32]; /* Make sure we're in the top level menu */ SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); /* Expect ">" */ EXPECT(ms->rdfd, Prompt, 5); /* Request menu 1 (Device Control) */ SEND(ms->wrfd, "1\r"); /* Select requested outlet */ EXPECT(ms->rdfd, Prompt, 5); snprintf(unum, sizeof(unum), "%i\r", outletNum); SEND(ms->wrfd, unum); /* Select menu 1 (Control Outlet) */ EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "1\r"); /* Select menu 3 (Immediate Reboot) */ EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "3\r"); /* Expect "Press " or "Enter 'YES'" (if confirmation turned on) */ retry: switch (StonithLookFor(ms->rdfd, Processing, 5)) { case 0: /* Got "Press " Do so */ SEND(ms->wrfd, "\r"); break; case 1: /* Got that annoying command confirmation :-( */ SEND(ms->wrfd, "YES\r"); goto retry; default: return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } LOG(PIL_INFO, "Host being rebooted: %s", host); /* Expect ">" */ if (StonithLookFor(ms->rdfd, Prompt, 10) < 0) { return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } /* All Right! Power is back on. Life is Good! */ LOG(PIL_INFO, "Power restored to host: %s", host); /* Return to top level menu */ SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); return(S_OK); } #if defined(ST_POWERON) && defined(ST_POWEROFF) static int apcmaster_onoff(struct pluginDevice* ms, int outletNum, const char * unitid, int req) { char unum[32]; const char * onoff = (req == ST_POWERON ? "1\r" : "2\r"); int rc; if ((rc = MSRobustLogin(ms) != S_OK)) { LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo); return(rc); } /* Make sure we're in the top level menu */ SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); /* Expect ">" */ EXPECT(ms->rdfd, Prompt, 5); /* Request menu 1 (Device Control) */ SEND(ms->wrfd, "1\r"); /* Select requested outlet */ snprintf(unum, sizeof(unum), "%d\r", outletNum); SEND(ms->wrfd, unum); /* Select menu 1 (Control Outlet) */ SEND(ms->wrfd, "1\r"); /* Send ON/OFF command for given outlet */ SEND(ms->wrfd, onoff); /* Expect "Press " or "Enter 'YES'" (if confirmation turned on) */ retry: switch (StonithLookFor(ms->rdfd, Processing, 5)) { case 0: /* Got "Press " Do so */ SEND(ms->wrfd, "\r"); break; case 1: /* Got that annoying command confirmation :-( */ SEND(ms->wrfd, "YES\r"); goto retry; default: return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } EXPECT(ms->rdfd, Prompt, 10); /* All Right! Command done. Life is Good! */ LOG(PIL_INFO, "Power to MS outlet(s) %d turned %s", outletNum, onoff); /* Pop back to main menu */ SEND(ms->wrfd, "\033\033\033\033\033\033\033\r"); return(S_OK); } #endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */ /* * Map the given host name into an (AC) Outlet number on the power strip */ static int MSNametoOutlet(struct pluginDevice* ms, const char * name) { char NameMapping[128]; int sockno; char sockname[32]; int times = 0; int ret = -1; /* Verify that we're in the top-level menu */ EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); /* Expect ">" */ EXPECT(ms->rdfd, Prompt, 5); /* Request menu 1 (Device Control) */ SEND(ms->wrfd, "1\r"); /* Expect: "-----" so we can skip over it... */ EXPECT(ms->rdfd, Separator, 5); EXPECT(ms->rdfd, CRNL, 5); EXPECT(ms->rdfd, CRNL, 5); /* Looks Good! Parse the status output */ do { times++; NameMapping[0] = EOS; SNARF(ms->rdfd, NameMapping, 5); if (sscanf(NameMapping , "%d- %23c",&sockno, sockname) == 2) { char * last = sockname+23; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (strcasecmp(name, sockname) == 0) { ret = sockno; } } } while (strlen(NameMapping) > 2 && times < 8); /* Pop back out to the top level menu */ EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); EXPECT(ms->rdfd, Prompt, 5); SEND(ms->wrfd, "\033"); return(ret); } static int apcmaster_status(StonithPlugin *s) { struct pluginDevice* ms; int rc; ERRIFNOTCONFIGED(s,S_OOPS); ms = (struct pluginDevice*) s; if ((rc = MSRobustLogin(ms) != S_OK)) { LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo); return(rc); } /* Expect ">" */ SEND(ms->wrfd, "\033\r"); EXPECT(ms->rdfd, Prompt, 5); return(MSLogout(ms)); } /* * Return the list of hosts (outlet names) for the devices on this MS unit */ static char ** apcmaster_hostlist(StonithPlugin *s) { char NameMapping[128]; char* NameList[64]; unsigned int numnames = 0; char ** ret = NULL; struct pluginDevice* ms; unsigned int i; ERRIFNOTCONFIGED(s,NULL); ms = (struct pluginDevice*) s; if (MSRobustLogin(ms) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo); return(NULL); } /* Expect ">" */ NULLEXPECT(ms->rdfd, Prompt, 10); /* Request menu 1 (Device Control) */ SEND(ms->wrfd, "1\r"); /* Expect: "-----" so we can skip over it... */ NULLEXPECT(ms->rdfd, Separator, 5); NULLEXPECT(ms->rdfd, CRNL, 5); NULLEXPECT(ms->rdfd, CRNL, 5); /* Looks Good! Parse the status output */ do { int sockno; char sockname[64]; NameMapping[0] = EOS; NULLSNARF(ms->rdfd, NameMapping, 5); if (sscanf(NameMapping , "%d- %23c",&sockno, sockname) == 2) { char * last = sockname+23; char * nm; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (numnames >= DIMOF(NameList)-1) { break; } if ((nm = (char*)STRDUP(sockname)) == NULL) { goto out_of_memory; } g_strdown(nm); NameList[numnames] = nm; ++numnames; NameList[numnames] = NULL; } } while (strlen(NameMapping) > 2); /* Pop back out to the top level menu */ SEND(ms->wrfd, "\033"); NULLEXPECT(ms->rdfd, Prompt, 10); SEND(ms->wrfd, "\033"); NULLEXPECT(ms->rdfd, Prompt, 10); SEND(ms->wrfd, "\033"); NULLEXPECT(ms->rdfd, Prompt, 10); SEND(ms->wrfd, "\033"); NULLEXPECT(ms->rdfd, Prompt, 10); if (numnames >= 1) { ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { goto out_of_memory; }else{ memcpy(ret, NameList, (numnames+1)*sizeof(char*)); } } (void)MSLogout(ms); return(ret); out_of_memory: LOG(PIL_CRIT, "out of memory"); for (i=0; iOpenStreamSocket(ms->device , TELNET_PORT, TELNET_SERVICE); if (fd < 0) { return(S_OOPS); } ms->rdfd = ms->wrfd = fd; return(S_OK); } /* * Reset the given host on this StonithPlugin device. */ static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host) { int rc = 0; int lorc = 0; struct pluginDevice* ms; ERRIFNOTCONFIGED(s,S_OOPS); ms = (struct pluginDevice*) s; if ((rc = MSRobustLogin(ms)) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo); return(rc); }else{ int noutlet; noutlet = MSNametoOutlet(ms, host); if (noutlet < 1) { LOG(PIL_WARN, "%s doesn't control host [%s]" , ms->device, host); return(S_BADHOST); } switch(request) { #if defined(ST_POWERON) && defined(ST_POWEROFF) case ST_POWERON: rc = apcmaster_onoff(ms, noutlet, host, request); break; case ST_POWEROFF: rc = apcmaster_onoff(ms, noutlet, host, request); break; #endif case ST_GENERIC_RESET: rc = MSReset(ms, noutlet, host); break; default: rc = S_INVAL; break; } } lorc = MSLogout(ms); return(rc != S_OK ? rc : lorc); } /* * Get the configuration parameters names */ static const char * const * apcmaster_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL}; return ret; } /* * Set the configuration parameters */ static int apcmaster_set_config(StonithPlugin * s, StonithNVpair * list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {ST_LOGIN, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s, S_OOPS); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->device = namestocopy[0].s_value; sd->user = namestocopy[1].s_value; sd->passwd = namestocopy[2].s_value; return(S_OK); } static const char * apcmaster_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* ms; const char * ret; ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ ms = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = ms->idinfo; break; case ST_DEVICENAME: /* Which particular device? */ ret = ms->device; break; case ST_DEVICEDESCR: ret = "APC MasterSwitch (via telnet)\n" "NOTE: The APC MasterSwitch accepts only one (telnet)\n" "connection/session a time. When one session is active,\n" "subsequent attempts to connect to the MasterSwitch" " will fail."; break; case ST_DEVICEURL: ret = "http://www.apc.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = apcmasterXML; break; default: ret = NULL; break; } return ret; } /* * APC MasterSwitch StonithPlugin destructor... */ static void apcmaster_destroy(StonithPlugin *s) { struct pluginDevice* ms; VOIDERRIFWRONGDEV(s); ms = (struct pluginDevice *)s; ms->pluginid = NOTpluginID; if (ms->rdfd >= 0) { close(ms->rdfd); ms->rdfd = -1; } if (ms->wrfd >= 0) { close(ms->wrfd); ms->wrfd = -1; } if (ms->device != NULL) { FREE(ms->device); ms->device = NULL; } if (ms->user != NULL) { FREE(ms->user); ms->user = NULL; } if (ms->passwd != NULL) { FREE(ms->passwd); ms->passwd = NULL; } FREE(ms); } /* Create a new APC Master Switch StonithPlugin device. */ static StonithPlugin * apcmaster_new(const char *subplugin) { struct pluginDevice* ms = ST_MALLOCT(struct pluginDevice); if (ms == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(ms, 0, sizeof(*ms)); ms->pluginid = pluginid; ms->pid = -1; ms->rdfd = -1; ms->wrfd = -1; ms->user = NULL; ms->device = NULL; ms->passwd = NULL; ms->idinfo = DEVICE; ms->sp.s_ops = &apcmasterOps; return(&(ms->sp)); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/apcmastersnmp.c0000644000000000000000000005466412120057602030240 0ustar00usergroup00000000000000/* * Stonith module for APC Masterswitch (SNMP) * Copyright (c) 2001 Andreas Piesk * Mangled by Sun Jiang Dong , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include /* device ID */ #define DEVICE "APC MasterSwitch (SNMP)" #include "stonith_plugin_common.h" #undef FREE /* defined by snmp stuff */ #ifdef PACKAGE_BUGREPORT #undef PACKAGE_BUGREPORT #endif #ifdef PACKAGE_NAME #undef PACKAGE_NAME #endif #ifdef PACKAGE_STRING #undef PACKAGE_STRING #endif #ifdef PACKAGE_TARNAME #undef PACKAGE_TARNAME #endif #ifdef PACKAGE_VERSION #undef PACKAGE_VERSION #endif #ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H # include # include # include # define INIT_AGENT() init_master_agent() #else # include # include # include # ifndef NETSNMP_DS_APPLICATION_ID # define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID # endif # ifndef NETSNMP_DS_AGENT_ROLE # define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE # endif # define netsnmp_ds_set_boolean ds_set_boolean # define INIT_AGENT() init_master_agent(161, NULL, NULL) #endif #define PIL_PLUGIN apcmastersnmp #define PIL_PLUGIN_S "apcmastersnmp" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #define DEBUGCALL \ if (Debug) { \ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \ } static StonithPlugin * apcmastersnmp_new(const char *); static void apcmastersnmp_destroy(StonithPlugin *); static const char * const * apcmastersnmp_get_confignames(StonithPlugin *); static int apcmastersnmp_set_config(StonithPlugin *, StonithNVpair *); static const char * apcmastersnmp_getinfo(StonithPlugin * s, int InfoType); static int apcmastersnmp_status(StonithPlugin * ); static int apcmastersnmp_reset_req(StonithPlugin * s, int request, const char * host); static char ** apcmastersnmp_hostlist(StonithPlugin *); static struct stonith_ops apcmastersnmpOps ={ apcmastersnmp_new, /* Create new STONITH object */ apcmastersnmp_destroy, /* Destroy STONITH object */ apcmastersnmp_getinfo, /* Return STONITH info string */ apcmastersnmp_get_confignames, /* Get configuration parameters */ apcmastersnmp_set_config, /* Set configuration */ apcmastersnmp_status, /* Return STONITH device status */ apcmastersnmp_reset_req, /* Request a reset */ apcmastersnmp_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; DEBUGCALL; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &apcmastersnmpOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * APCMaster tested with APC Masterswitch 9212 */ /* outlet commands / status codes */ #define OUTLET_ON 1 #define OUTLET_OFF 2 #define OUTLET_REBOOT 3 #define OUTLET_NO_CMD_PEND 2 /* oids */ #define OID_IDENT ".1.3.6.1.4.1.318.1.1.12.1.5.0" #define OID_NUM_OUTLETS ".1.3.6.1.4.1.318.1.1.12.1.8.0" #define OID_OUTLET_NAMES ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.%i" #define OID_OUTLET_STATE ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%i" #define OID_OUTLET_COMMAND_PENDING ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.%i" #define OID_OUTLET_REBOOT_DURATION ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.%i" /* snmpset -c private -v1 172.16.0.32:161 ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1 The last octet in the OID is the plug number. The value can be 1 thru 8 because there are 8 power plugs on this device. The integer that can be set is as follows: 1=on, 2=off, and 3=reset */ /* own defines */ #define MAX_STRING 128 #define ST_PORT "port" /* structur of stonith object */ struct pluginDevice { StonithPlugin sp; /* StonithPlugin object */ const char* pluginid; /* id of object */ const char* idinfo; /* type of device */ struct snmp_session* sptr; /* != NULL->session created */ char * hostname; /* masterswitch's hostname */ /* or ip addr */ int port; /* snmp port */ char * community; /* snmp community (r/w) */ int num_outlets; /* number of outlets */ }; /* for checking hardware (issue a warning if mismatch) */ static const char* APC_tested_ident[] = {"AP9606","AP7920","AP7921","AP7900","AP_other_well_tested"}; /* constant strings */ static const char *pluginid = "APCMS-SNMP-Stonith"; static const char *NOTpluginID = "APCMS SNMP device has been destroyed"; #include "stonith_config_xml.h" #define XML_PORT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_PORT \ XML_PARM_SHORTDESC_END #define XML_PORT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The port number on which the SNMP server is running on the STONITH device" \ XML_PARM_LONGDESC_END #define XML_PORT_PARM \ XML_PARAMETER_BEGIN(ST_PORT, "string", "1") \ XML_PORT_SHORTDESC \ XML_PORT_LONGDESC \ XML_PARAMETER_END static const char *apcmastersnmpXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_PORT_PARM XML_COMMUNITY_PARM XML_PARAMETERS_END; /* * own prototypes */ static void APC_error(struct snmp_session *sptr, const char *fn , const char *msg); static struct snmp_session *APC_open(char *hostname, int port , char *community); static void *APC_read(struct snmp_session *sptr, const char *objname , int type); static int APC_write(struct snmp_session *sptr, const char *objname , char type, char *value); static void APC_error(struct snmp_session *sptr, const char *fn, const char *msg) { int snmperr = 0; int cliberr = 0; char *errstr; snmp_error(sptr, &cliberr, &snmperr, &errstr); LOG(PIL_CRIT , "%s: %s (cliberr: %i / snmperr: %i / error: %s)." , fn, msg, cliberr, snmperr, errstr); free(errstr); } /* * creates a snmp session */ static struct snmp_session * APC_open(char *hostname, int port, char *community) { static struct snmp_session session; struct snmp_session *sptr; DEBUGCALL; /* create session */ snmp_sess_init(&session); /* fill session */ session.peername = hostname; session.version = SNMP_VERSION_1; session.remote_port = port; session.community = (u_char *)community; session.community_len = strlen(community); session.retries = 5; session.timeout = 1000000; /* open session */ sptr = snmp_open(&session); if (sptr == NULL) { APC_error(&session, __FUNCTION__, "cannot open snmp session"); } /* return pointer to opened session */ return (sptr); } /* * parse config */ /* * read value of given oid and return it as string */ static void * APC_read(struct snmp_session *sptr, const char *objname, int type) { oid name[MAX_OID_LEN]; size_t namelen = MAX_OID_LEN; struct variable_list *vars; struct snmp_pdu *pdu; struct snmp_pdu *resp; static char response_str[MAX_STRING]; static int response_int; DEBUGCALL; /* convert objname into oid; return NULL if invalid */ if (!read_objid(objname, name, &namelen)) { LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname); return (NULL); } /* create pdu */ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) { /* get-request have no values */ snmp_add_null_var(pdu, name, namelen); /* send pdu and get response; return NULL if error */ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) { /* request succeed, got valid response ? */ if (resp->errstat == SNMP_ERR_NOERROR) { /* go through the returned vars */ for (vars = resp->variables; vars; vars = vars->next_variable) { /* return response as string */ if ((vars->type == type) && (type == ASN_OCTET_STR)) { memset(response_str, 0, MAX_STRING); strncpy(response_str, (char *)vars->val.string, MIN(vars->val_len, MAX_STRING)); snmp_free_pdu(resp); return ((void *) response_str); } /* return response as integer */ if ((vars->type == type) && (type == ASN_INTEGER)) { response_int = *vars->val.integer; snmp_free_pdu(resp); return ((void *) &response_int); } } }else{ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]." , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat)); } }else{ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu"); } /* free repsonse pdu (necessary?) */ snmp_free_pdu(resp); }else{ APC_error(sptr, __FUNCTION__, "cannot create pdu"); } /* error: return nothing */ return (NULL); } /* * write value of given oid */ static int APC_write(struct snmp_session *sptr, const char *objname, char type, char *value) { oid name[MAX_OID_LEN]; size_t namelen = MAX_OID_LEN; struct snmp_pdu *pdu; struct snmp_pdu *resp; DEBUGCALL; /* convert objname into oid; return FALSE if invalid */ if (!read_objid(objname, name, &namelen)) { LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname); return (FALSE); } /* create pdu */ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) { /* add to be written value to pdu */ snmp_add_var(pdu, name, namelen, type, value); /* send pdu and get response; return NULL if error */ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) { /* go through the returned vars */ if (resp->errstat == SNMP_ERR_NOERROR) { /* request successful done */ snmp_free_pdu(resp); return (TRUE); }else{ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]." , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat)); } }else{ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu"); } /* free pdu (again: necessary?) */ snmp_free_pdu(resp); }else{ APC_error(sptr, __FUNCTION__, "cannot create pdu"); } /* error */ return (FALSE); } /* * return the status for this device */ static int apcmastersnmp_status(StonithPlugin * s) { struct pluginDevice *ad; char *ident; int i; DEBUGCALL; ERRIFNOTCONFIGED(s, S_OOPS); ad = (struct pluginDevice *) s; if ((ident = APC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__); return (S_ACCESS); } /* issue a warning if ident mismatches */ for(i=DIMOF(APC_tested_ident) -1; i >=0 ; i--) { if (strcmp(ident, APC_tested_ident[i]) == 0) { break; } } if (i<0) { LOG(PIL_WARN , "%s: module not tested with this hardware '%s'." , __FUNCTION__, ident); } /* status ok */ return (S_OK); } /* * return the list of hosts configured for this device */ static char ** apcmastersnmp_hostlist(StonithPlugin * s) { char **hl; struct pluginDevice *ad; int j, h, num_outlets; char *outlet_name; char objname[MAX_STRING]; DEBUGCALL; ERRIFNOTCONFIGED(s, NULL); ad = (struct pluginDevice *) s; /* allocate memory for array of up to NUM_OUTLETS strings */ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); return (NULL); } /* clear hostlist array */ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *)); num_outlets = 0; /* read NUM_OUTLETS values and put them into hostlist array */ for (j = 0; j < ad->num_outlets; ++j) { /* prepare objname */ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, j + 1); /* read outlet name */ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read name for outlet %d." , __FUNCTION__, j+1); stonith_free_hostlist(hl); hl = NULL; return (hl); } /* Check whether the host is already listed */ for (h = 0; h < num_outlets; ++h) { if (strcasecmp(hl[h],outlet_name) == 0) break; } if (h >= num_outlets) { /* put outletname in hostlist */ if (Debug) { LOG(PIL_DEBUG, "%s: added %s to hostlist." , __FUNCTION__, outlet_name); } if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); stonith_free_hostlist(hl); hl = NULL; return (hl); } g_strdown(hl[num_outlets]); num_outlets++; } } if (Debug) { LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets." , __FUNCTION__, num_outlets, j); } /* return list */ return (hl); } /* * reset the host */ static int apcmastersnmp_reset_req(StonithPlugin * s, int request, const char *host) { struct pluginDevice *ad; char objname[MAX_STRING]; char value[MAX_STRING]; char *outlet_name; int req_oid = OUTLET_REBOOT; int expect_state = OUTLET_ON; int i, h, num_outlets, outlet, reboot_duration, *state, bad_outlets; int outlets[8]; /* Assume that one node is connected to a maximum of 8 outlets */ DEBUGCALL; ERRIFNOTCONFIGED(s, S_OOPS); ad = (struct pluginDevice *) s; num_outlets = 0; reboot_duration = 0; bad_outlets = 0; /* read max. as->num_outlets values */ for (outlet = 1; outlet <= ad->num_outlets; outlet++) { /* prepare objname */ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, outlet); /* read outlet name */ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read name for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } if (Debug) { LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name); } /* found one */ if (strcasecmp(outlet_name, host) == 0) { if (Debug) { LOG(PIL_DEBUG, "%s: found %s at outlet %d." , __FUNCTION__, host, outlet); } /* Check that the outlet is not administratively down */ /* prepare objname */ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet); /* get outlet's state */ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) { LOG(PIL_CRIT , "%s: cannot read state for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } /* prepare oid */ snprintf(objname, MAX_STRING, OID_OUTLET_REBOOT_DURATION , outlet); /* read reboot duration of the port */ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) { LOG(PIL_CRIT , "%s: cannot read reboot duration for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } if (num_outlets == 0) { /* save the inital value of the first port */ reboot_duration = *state; } else if (reboot_duration != *state) { LOG(PIL_WARN, "%s: outlet %d has a different reboot duration!" , __FUNCTION__, outlet); if (reboot_duration < *state) reboot_duration = *state; } /* Ok, add it to the list of outlets to control */ outlets[num_outlets]=outlet; num_outlets++; } } if (Debug) { LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet); } /* host not found in outlet names */ if (num_outlets < 1) { LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host); return (S_BADHOST); } /* choose the OID for the stonith request */ switch (request) { case ST_POWERON: req_oid = OUTLET_ON; expect_state = OUTLET_ON; break; case ST_POWEROFF: req_oid = OUTLET_OFF; expect_state = OUTLET_OFF; break; case ST_GENERIC_RESET: req_oid = OUTLET_REBOOT; expect_state = OUTLET_ON; break; default: break; } /* Turn them all off */ for (outlet=outlets[0], i=0 ; i < num_outlets; i++, outlet = outlets[i]) { /* prepare objname */ snprintf(objname, MAX_STRING, OID_OUTLET_COMMAND_PENDING, outlet); /* are there pending commands ? */ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) { LOG(PIL_CRIT, "%s: cannot read pending commands for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } if (*state != OUTLET_NO_CMD_PEND) { LOG(PIL_CRIT, "%s: command pending.", __FUNCTION__); return (S_RESETFAIL); } /* prepare objnames */ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet); snprintf(value, MAX_STRING, "%i", req_oid); /* send reboot cmd */ if (!APC_write(ad->sptr, objname, 'i', value)) { LOG(PIL_CRIT , "%s: cannot send reboot command for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } } /* wait max. 2*reboot_duration for all outlets to go back on */ for (i = 0; i < reboot_duration << 1; i++) { sleep(1); bad_outlets = 0; for (outlet=outlets[0], h=0 ; h < num_outlets; h++, outlet = outlets[h]) { /* prepare objname of the first outlet */ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet); /* get outlet's state */ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) { LOG(PIL_CRIT , "%s: cannot read state for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } if (*state != expect_state) bad_outlets++; } if (bad_outlets == 0) return (S_OK); } if (bad_outlets == num_outlets) { /* reset failed */ LOG(PIL_CRIT, "%s: stonith operation for '%s' failed." , __FUNCTION__, host); return (S_RESETFAIL); } else { /* Not all outlets back on, but at least one; implies node was */ /* rebooted correctly */ LOG(PIL_WARN,"%s: Not all outlets in the expected state!" , __FUNCTION__); return (S_OK); } } /* * Get the configuration parameter names. */ static const char * const * apcmastersnmp_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, NULL}; return ret; } /* * Set the configuration parameters. */ static int apcmastersnmp_set_config(StonithPlugin * s, StonithNVpair * list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; int * i; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {ST_PORT, NULL} , {ST_COMMUNITY, NULL} , {NULL, NULL} }; DEBUGCALL; ERRIFWRONGDEV(s,S_INVAL); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->hostname = namestocopy[0].s_value; sd->port = atoi(namestocopy[1].s_value); PluginImports->mfree(namestocopy[1].s_value); sd->community = namestocopy[2].s_value; /* try to resolve the hostname/ip-address */ if (gethostbyname(sd->hostname) != NULL) { /* init snmp library */ init_snmp("apcmastersnmp"); /* now try to get a snmp session */ if ((sd->sptr = APC_open(sd->hostname, sd->port, sd->community)) != NULL) { /* ok, get the number of outlets from the masterswitch */ if ((i = APC_read(sd->sptr, OID_NUM_OUTLETS, ASN_INTEGER)) == NULL) { LOG(PIL_CRIT , "%s: cannot read number of outlets." , __FUNCTION__); return (S_ACCESS); } /* store the number of outlets */ sd->num_outlets = *i; if (Debug) { LOG(PIL_DEBUG, "%s: number of outlets: %i." , __FUNCTION__, sd->num_outlets ); } /* Everything went well */ return (S_OK); }else{ LOG(PIL_CRIT, "%s: cannot create snmp session." , __FUNCTION__); } }else{ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d." , __FUNCTION__, sd->hostname, h_errno); } /* not a valid config */ return (S_BADCONFIG); } /* * get info about the stonith device */ static const char * apcmastersnmp_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice *ad; const char *ret = NULL; DEBUGCALL; ERRIFWRONGDEV(s, NULL); ad = (struct pluginDevice *) s; switch (reqtype) { case ST_DEVICEID: ret = ad->idinfo; break; case ST_DEVICENAME: ret = ad->hostname; break; case ST_DEVICEDESCR: ret = "APC MasterSwitch (via SNMP)\n" "The APC MasterSwitch can accept multiple simultaneous SNMP clients"; break; case ST_DEVICEURL: ret = "http://www.apc.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = apcmastersnmpXML; break; } return ret; } /* * APC StonithPlugin destructor... */ static void apcmastersnmp_destroy(StonithPlugin * s) { struct pluginDevice *ad; DEBUGCALL; VOIDERRIFWRONGDEV(s); ad = (struct pluginDevice *) s; ad->pluginid = NOTpluginID; /* release snmp session */ if (ad->sptr != NULL) { snmp_close(ad->sptr); ad->sptr = NULL; } /* reset defaults */ if (ad->hostname != NULL) { PluginImports->mfree(ad->hostname); ad->hostname = NULL; } if (ad->community != NULL) { PluginImports->mfree(ad->community); ad->community = NULL; } ad->num_outlets = 0; PluginImports->mfree(ad); } /* * Create a new APC StonithPlugin device. Too bad this function can't be * static */ static StonithPlugin * apcmastersnmp_new(const char *subplugin) { struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice); DEBUGCALL; /* no memory for stonith-object */ if (ad == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); return (NULL); } /* clear stonith-object */ memset(ad, 0, sizeof(*ad)); /* set defaults */ ad->pluginid = pluginid; ad->sptr = NULL; ad->hostname = NULL; ad->community = NULL; ad->idinfo = DEVICE; ad->sp.s_ops = &apcmastersnmpOps; /* return the object */ return (&(ad->sp)); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/apcmastersnmp.cfg.example0000644000000000000000000000334112120057602032171 0ustar00usergroup00000000000000# # this is an example config for the stonith module apcmastersnmp # # 1. what does the fields on the line mean ? # # all parameters must be given on a single line. blank lines and lines # starting with '#' are ignored. only the first not ignored line will # be processed. all subsequent lines will be ignored. the different # fields must be seperated by white-spaces (blanks and/or tabs). # # the first field is the either the hostname or the ip address. the # hostname must be resolvable. the second fields specifies the snmp port # the masterswitch is listening. for snmp the default is 161. the last # field contains the so called 'community' string. this must be the same # as the one in the masterswitch configuration. # # # 2. how must the masterswitch be configured ? # # as said above, the community string must be set to the same value entered # in this config. the different outlets must be named after the connected # hosts. that means, the outlet names must be the same as the node names # in /etc/ha.d/ha.cf. the reset values should be set to reasonable values. # # the module DON'T configure the module in any way! # # # 3. how does the module work ? # # in case of a stonith the module receives the nodename of the host, which # should be reset. the module looks up this nodename in the list of outlet # names. that's why the names must be identical (see 2.). if it finds the # name, it'll reset the appropriate outlet using the configured values # (eg. delay, duration). then the module waits for the outlet to coming # up. if it comes up, a successful stonith will be reported back. otherwise # the stonith failed and a failure code will be returned. # 192.168.1.110 161 private Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/apcsmart.c0000644000000000000000000005572712120057602027176 0ustar00usergroup00000000000000/* * Stonith module for APCSmart Stonith device * Copyright (c) 2000 Andreas Piesk * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original version of this UPS code was taken from: * 'Network UPS Tools' by Russell Kroll * homepage: http://www.networkupstools.org/ * * Significantly mangled by Alan Robertson */ #include #define DEVICE "APCSmart" #include "stonith_plugin_common.h" /* * APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000) * * The reset is a combined reset: "S" and "@000" * The "S" command tells the ups that if it is on-battery, it should * remain offline until the power is back. * If that command is not accepted, the "@000" command will be sent * to tell the ups to turn off and back on right away. * In both cases, if the UPS supports a 20 second shutdown grace * period (such as on the 900XLI), the shutdown will delay that long, * otherwise the shutdown will happen immediately (the code searches * for the smallest possible delay). */ #define CFG_FILE "/etc/ha.d/apcsmart.cfg" #define MAX_DEVICES 1 #define SERIAL_TIMEOUT 3 /* timeout in sec */ #define SEND_DELAY 50000 /* in microseconds */ #define ENDCHAR 10 /* use LF */ #define MAX_STRING 512 #define MAX_DELAY_STRING 16 #define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through * the values */ #define CMD_SMART_MODE "Y" #define RSP_SMART_MODE "SM" #define CMD_GET_STATUS "Q" #define RSP_GET_STATUS NULL #define CMD_RESET "S" /* turn off & stay off if on battery */ #define CMD_RESET2 "@000" /* turn off & immediately turn on */ #define RSP_RESET "*" /* RESET response from older models */ #define RSP_RESET2 "OK" /* RESET response from newer models */ #define RSP_NA "NA" #define CMD_READREG1 "~" #define CMD_OFF "Z" #define CMD_ON "\016" /* (control-n) */ #define CMD_SHUTDOWN_DELAY "p" #define CMD_WAKEUP_DELAY "r" #define CR 13 struct pluginDevice { StonithPlugin sp; const char * pluginid; /* of object */ const char * idinfo; /* type of device */ char ** hostlist; /* served by the device (only 1) */ int hostcount;/* of hosts (1) */ char * upsdev; /* */ int upsfd; /* for serial port */ int retries; char shutdown_delay[MAX_DELAY_STRING]; char old_shutdown_delay[MAX_DELAY_STRING]; char wakeup_delay[MAX_DELAY_STRING]; char old_wakeup_delay[MAX_DELAY_STRING]; }; /* saving old settings */ /* FIXME! These should be part of pluginDevice struct above */ static struct termios old_tio; static int f_serialtimeout; /* flag for timeout */ static const char *pluginid = "APCSmart-Stonith"; static const char *NOTpluginID = "APCSmart device has been destroyed"; /* * stonith prototypes */ #define PIL_PLUGIN apcsmart #define PIL_PLUGIN_S "apcsmart" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" static StonithPlugin * apcsmart_new(const char *); static void apcsmart_destroy(StonithPlugin *); static const char * const * apcsmart_get_confignames(StonithPlugin*); static int apcsmart_set_config(StonithPlugin *, StonithNVpair*); static const char * apcsmart_get_info(StonithPlugin * s, int InfoType); static int apcsmart_status(StonithPlugin * ); static int apcsmart_reset_req(StonithPlugin * s, int request, const char * host); static char ** apcsmart_hostlist(StonithPlugin *); static struct stonith_ops apcsmartOps ={ apcsmart_new, /* Create new STONITH object */ apcsmart_destroy, /* Destroy STONITH object */ apcsmart_get_info, /* Return STONITH info string */ apcsmart_get_confignames, /* Return STONITH info string */ apcsmart_set_config, /* Get configuration from NVpairs */ apcsmart_status, /* Return STONITH device status */ apcsmart_reset_req, /* Request a reset */ apcsmart_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &apcsmartOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } #include "stonith_config_xml.h" static const char *apcsmartXML = XML_PARAMETERS_BEGIN XML_TTYDEV_PARM XML_HOSTLIST_PARM XML_PARAMETERS_END; /* * own prototypes */ int APC_open_serialport(const char *port, speed_t speed); void APC_close_serialport(const char *port, int upsfd); void APC_sh_serial_timeout(int sig); int APC_send_cmd(int upsfd, const char *cmd); int APC_recv_rsp(int upsfd, char *rsp); int APC_enter_smartmode(int upsfd); int APC_set_ups_var(int upsfd, const char *cmd, char *newval); int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay); int APC_init( struct pluginDevice *ad ); void APC_deinit( struct pluginDevice *ad ); /* * Signal handler for serial port timeouts */ void APC_sh_serial_timeout(int sig) { if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } STONITH_IGNORE_SIG(SIGALRM); if (Debug) { LOG(PIL_DEBUG, "%s: serial port timed out.", __FUNCTION__); } f_serialtimeout = TRUE; return; } /* * Open serial port and set it to b2400 */ int APC_open_serialport(const char *port, speed_t speed) { struct termios tio; int fd; int rc; int errno_save; int fflags; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if ((rc = OurImports->TtyLock(port)) < 0) { LOG(PIL_CRIT, "%s: Could not lock tty %s [rc=%d]." , __FUNCTION__, port, rc); return -1; } STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout); alarm(SERIAL_TIMEOUT); f_serialtimeout = FALSE; fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL); errno_save = errno; alarm(0); STONITH_IGNORE_SIG(SIGALRM); if (fd < 0) { LOG(PIL_CRIT, "%s: Open of %s %s [%s].", __FUNCTION__ , port , f_serialtimeout ? "timed out" : "failed" , strerror(errno_save)); OurImports->TtyUnlock(port); return -1; } if ((fflags = fcntl(fd, F_GETFL)) < 0 || fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) { LOG(PIL_CRIT, "%s: Setting flags on %s failed [%s]." , __FUNCTION__ , port , strerror(errno_save)); close(fd); OurImports->TtyUnlock(port); return -1; } if (tcgetattr(fd, &old_tio) < 0) { LOG(PIL_CRIT, "%s: tcgetattr of %s failed [%s].", __FUNCTION__ , port , strerror(errno)); close(fd); OurImports->TtyUnlock(port); return -1; } memcpy(&tio, &old_tio, sizeof(struct termios)); tio.c_cflag = CS8 | CLOCAL | CREAD; tio.c_iflag = IGNPAR; tio.c_oflag = 0; tio.c_lflag = 0; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, speed); cfsetospeed(&tio, speed); tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &tio); return (fd); } /* * Close serial port and restore old settings */ void APC_close_serialport(const char *port, int upsfd) { if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (upsfd < 0) { return; } tcflush(upsfd, TCIFLUSH); tcsetattr(upsfd, TCSANOW, &old_tio); close(upsfd); if (port != NULL) { OurImports->TtyUnlock(port); } } /* * Send a command to the ups */ int APC_send_cmd(int upsfd, const char *cmd) { int i; if (Debug) { LOG(PIL_DEBUG, "%s(\"%s\")", __FUNCTION__, cmd); } tcflush(upsfd, TCIFLUSH); for (i = strlen(cmd); i > 0; i--) { if (write(upsfd, cmd++, 1) != 1) { return (S_ACCESS); } usleep(SEND_DELAY); } return (S_OK); } /* * Get the response from the ups */ int APC_recv_rsp(int upsfd, char *rsp) { char *p = rsp; char inp; int num = 0; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } *p = '\0'; STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout); alarm(SERIAL_TIMEOUT); while (num < MAX_STRING) { if (read(upsfd, &inp, 1) == 1) { /* shutdown sends only a '*' without LF */ if ((inp == '*') && (num == 0)) { *p++ = inp; num++; inp = ENDCHAR; } if (inp == ENDCHAR) { alarm(0); STONITH_IGNORE_SIG(SIGALRM); *p = '\0'; if (Debug) { LOG(PIL_DEBUG, "return(\"%s\")/*%s*/;" , rsp, __FUNCTION__); } return (S_OK); } if (inp != CR) { *p++ = inp; num++; } }else{ alarm(0); STONITH_IGNORE_SIG(SIGALRM); *p = '\0'; LOG(PIL_DEBUG, "%s: %s.", __FUNCTION__, f_serialtimeout ? "timeout" : "can't access device" ); return (f_serialtimeout ? S_TIMEOUT : S_ACCESS); } } return (S_ACCESS); } /* * Enter smart mode */ int APC_enter_smartmode(int upsfd) { int rc; char resp[MAX_STRING]; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } strcpy(resp, RSP_SMART_MODE); if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK) && ((rc = APC_recv_rsp(upsfd, resp)) == S_OK) && (strcmp(RSP_SMART_MODE, resp) == 0)) { return (S_OK); } return (S_ACCESS); } /* * Set a value in the hardware using the '-' (repeat) approach */ int APC_set_ups_var(int upsfd, const char *cmd, char *newval) { char resp[MAX_STRING]; char orig[MAX_STRING]; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) { return (rc); } if (Debug) { LOG(PIL_DEBUG, "%s: var '%s' original val %s" , __FUNCTION__, cmd, orig); } if (strcmp(orig, newval) == 0) { return (S_OK); /* already set */ } *resp = '\0'; while (strcmp(resp, orig) != 0) { if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) { return (rc); } if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) { return (rc); } if (strcmp(resp, newval) == 0) { if (Debug) { LOG(PIL_DEBUG, "%s: var '%s' set to %s" , __FUNCTION__, cmd, newval); } strcpy(newval, orig); /* return the old value */ return (S_OK); /* got it */ } } LOG(PIL_CRIT, "%s(): Could not set variable '%s' to %s!" , __FUNCTION__, cmd, newval); LOG(PIL_CRIT, "%s(): This UPS may not support STONITH :-(" , __FUNCTION__); return (S_OOPS); } /* * Query the smallest delay supported by the hardware using the * '-' (repeat) approach and looping through all possible values, * saving the smallest */ int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay) { char resp[MAX_DELAY_STRING]; char orig[MAX_DELAY_STRING]; int delay, smallest; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) { return (rc); } smallest = atoi(orig); strcpy(smdelay, orig); *resp = '\0'; /* search for smallest delay; need to loop through all possible * values so that we leave delay the way we found it */ while (strcmp(resp, orig) != 0) { if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) { return (rc); } if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) { return (rc); } if ((delay = atoi(resp)) < smallest) { smallest = delay; strcpy(smdelay, resp); } } return (S_OK); } /* * Initialize the ups */ int APC_init(struct pluginDevice *ad) { int upsfd; char value[MAX_DELAY_STRING]; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } /* if ad->upsfd != -1 device has already been configured. */ /* Just enter smart mode again because otherwise a SmartUPS-1000 */ /* has been observed to sometimes not respond. */ if(ad->upsfd >= 0) { if(APC_enter_smartmode(ad->upsfd) != S_OK) { return(S_OOPS); } return S_OK; } /* open serial port and store the fd in ad->upsfd */ if ((upsfd = APC_open_serialport(ad->upsdev, B2400)) == -1) { return S_OOPS; } /* switch into smart mode */ if (APC_enter_smartmode(upsfd) != S_OK) { APC_close_serialport(ad->upsdev, upsfd); ad->upsfd = -1; return S_OOPS; } /* get the smallest possible delays for this particular hardware */ if (APC_get_smallest_delay(upsfd, CMD_SHUTDOWN_DELAY , ad->shutdown_delay) != S_OK || APC_get_smallest_delay(upsfd, CMD_WAKEUP_DELAY , ad->wakeup_delay) != S_OK) { LOG(PIL_CRIT, "%s: couldn't retrieve smallest delay from UPS" , __FUNCTION__); APC_close_serialport(ad->upsdev, upsfd); ad->upsfd = -1; return S_OOPS; } /* get the old settings and store them */ strcpy(value, ad->shutdown_delay); if (APC_set_ups_var(upsfd, CMD_SHUTDOWN_DELAY, value) != S_OK) { LOG(PIL_CRIT, "%s: couldn't set shutdown delay to %s" , __FUNCTION__, ad->shutdown_delay); APC_close_serialport(ad->upsdev, upsfd); ad->upsfd = -1; return S_OOPS; } strcpy(ad->old_shutdown_delay, value); strcpy(value, ad->wakeup_delay); if (APC_set_ups_var(upsfd, CMD_WAKEUP_DELAY, value) != S_OK) { LOG(PIL_CRIT, "%s: couldn't set wakeup delay to %s" , __FUNCTION__, ad->wakeup_delay); APC_close_serialport(ad->upsdev, upsfd); ad->upsfd = -1; return S_OOPS; } strcpy(ad->old_wakeup_delay, value); ad->upsfd = upsfd; return S_OK; } /* * Restore original settings and close the port */ void APC_deinit(struct pluginDevice *ad) { APC_enter_smartmode( ad->upsfd ); APC_set_ups_var(ad->upsfd, CMD_SHUTDOWN_DELAY, ad->old_shutdown_delay); APC_set_ups_var(ad->upsfd, CMD_WAKEUP_DELAY, ad->old_wakeup_delay); /* close serial port */ if (ad->upsfd >= 0) { APC_close_serialport(ad->upsdev, ad->upsfd); ad->upsfd = -1; } } static const char * const * apcsmart_get_confignames(StonithPlugin* sp) { static const char * names[] = {ST_TTYDEV, ST_HOSTLIST, NULL}; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } return names; } /* * Stash away the config info we've been given... */ static int apcsmart_set_config(StonithPlugin * s, StonithNVpair* list) { struct pluginDevice * ad = (struct pluginDevice*)s; StonithNamesToGet namestocopy [] = { {ST_TTYDEV, NULL} , {ST_HOSTLIST, NULL} , {NULL, NULL} }; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s, S_OOPS); if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } ad->upsdev = namestocopy[0].s_value; ad->hostlist = OurImports->StringToHostList(namestocopy[1].s_value); FREE(namestocopy[1].s_value); if (ad->hostlist == NULL) { LOG(PIL_CRIT,"StringToHostList() failed"); return S_OOPS; } for (ad->hostcount = 0; ad->hostlist[ad->hostcount] ; ad->hostcount++) { g_strdown(ad->hostlist[ad->hostcount]); } if (access(ad->upsdev, R_OK|W_OK|F_OK) < 0) { LOG(PIL_CRIT,"Cannot access tty [%s]", ad->upsdev); return S_BADCONFIG; } return ad->hostcount ? S_OK : S_BADCONFIG; } /* * return the status for this device */ static int apcsmart_status(StonithPlugin * s) { struct pluginDevice *ad = (struct pluginDevice *) s; char resp[MAX_STRING]; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); /* get status */ if (((rc = APC_init( ad )) == S_OK) && ((rc = APC_send_cmd(ad->upsfd, CMD_GET_STATUS)) == S_OK) && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)) { return (S_OK); /* everything ok. */ } if (Debug) { LOG(PIL_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc); } return (rc); } /* * return the list of hosts configured for this device */ static char ** apcsmart_hostlist(StonithPlugin * s) { struct pluginDevice *ad = (struct pluginDevice *) s; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,NULL); return OurImports->CopyHostList((const char **)(void*)ad->hostlist); } static gboolean apcsmart_RegisterBitsSet(struct pluginDevice * ad, int nreg, unsigned bits , gboolean* waserr) { const char* reqregs[4] = {"?", "~", "'", "8"}; unsigned regval; char resp[MAX_STRING]; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (APC_enter_smartmode(ad->upsfd) != S_OK || APC_send_cmd(ad->upsfd, reqregs[nreg]) != S_OK || APC_recv_rsp(ad->upsfd, resp) != S_OK || (sscanf(resp, "%02x", ®val) != 1)) { if (waserr){ *waserr = TRUE; } return FALSE; } if (waserr){ *waserr = FALSE; } return ((regval & bits) == bits); } #define apcsmart_IsPoweredOff(ad, err) apcsmart_RegisterBitsSet(ad,1,0x40,err) #define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err) static int apcsmart_ReqOnOff(struct pluginDevice * ad, int request) { const char * cmdstr; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } cmdstr = (request == ST_POWEROFF ? CMD_OFF : CMD_ON); /* enter smartmode, send on/off command */ if ((rc =APC_enter_smartmode(ad->upsfd)) != S_OK || (rc = APC_send_cmd(ad->upsfd, cmdstr)) != S_OK) { return rc; } sleep(2); if ((rc = APC_send_cmd(ad->upsfd, cmdstr)) == S_OK) { gboolean ison; gboolean waserr; sleep(1); ison = !apcsmart_IsPoweredOff(ad, &waserr); if (waserr) { return S_RESETFAIL; } if (request == ST_POWEROFF) { return ison ? S_RESETFAIL : S_OK; }else{ return ison ? S_OK : S_RESETFAIL; } } return rc; } /* * reset the host */ static int apcsmart_ReqGenericReset(struct pluginDevice *ad) { char resp[MAX_STRING]; int rc = S_RESETFAIL; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } /* send reset command(s) */ if (((rc = APC_init(ad)) == S_OK) && ((rc = APC_send_cmd(ad->upsfd, CMD_RESET)) == S_OK)) { if (((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK) && (strcmp(resp, RSP_RESET) == 0 || strcmp(resp, RSP_RESET2) == 0)) { /* first kind of reset command was accepted */ } else if (((rc = APC_send_cmd(ad->upsfd, CMD_RESET2)) == S_OK) && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK) && (strcmp(resp, RSP_RESET) == 0 || strcmp(resp, RSP_RESET2) == 0)) { /* second kind of command was accepted */ } else { if (Debug) { LOG(PIL_DEBUG, "APC: neither reset command " "was accepted"); } rc = S_RESETFAIL; } } if (rc == S_OK) { /* we wait grace period + up to 10 seconds after shutdown */ int maxdelay = atoi(ad->shutdown_delay)+10; int j; for (j=0; j < maxdelay; ++j) { gboolean err; if (apcsmart_ResetHappening(ad, &err)) { return err ? S_RESETFAIL : S_OK; } sleep(1); } LOG(PIL_CRIT, "%s: timed out waiting for reset to end." , __FUNCTION__); return S_RESETFAIL; }else{ if (strcmp(resp, RSP_NA) == 0){ gboolean iserr; /* This means it's currently powered off */ /* or busy on a previous command... */ if (apcsmart_IsPoweredOff(ad, &iserr)) { if (iserr) { LOG(PIL_DEBUG, "%s: power off " "detection failed.", __FUNCTION__); return S_RESETFAIL; } if (Debug) { LOG(PIL_DEBUG, "APC: was powered off, " "powering back on."); } return apcsmart_ReqOnOff(ad, ST_POWERON); } } } strcpy(resp, "?"); /* reset failed */ return S_RESETFAIL; } static int apcsmart_reset_req(StonithPlugin * s, int request, const char *host) { char ** hl; int b_found=FALSE; struct pluginDevice * ad = (struct pluginDevice *) s; int rc; ERRIFNOTCONFIGED(s, S_OOPS); if (host == NULL) { LOG(PIL_CRIT, "%s: invalid hostname argument.", __FUNCTION__); return (S_INVAL); } /* look through the hostlist */ hl = ad->hostlist; while (*hl && !b_found ) { if( strcasecmp( *hl, host ) == 0 ) { b_found = TRUE; break; }else{ ++hl; } } /* host not found in hostlist */ if( !b_found ) { LOG(PIL_CRIT, "%s: host '%s' not in hostlist." , __FUNCTION__, host); return S_BADHOST; } if ((rc = APC_init(ad)) != S_OK) { return rc; } if (request == ST_POWERON || request == ST_POWEROFF) { return apcsmart_ReqOnOff(ad, request); } return apcsmart_ReqGenericReset(ad); } /* * get info about the stonith device */ static const char * apcsmart_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice *ad = (struct pluginDevice *) s; const char *ret; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); switch (reqtype) { case ST_DEVICEID: ret = ad->idinfo; break; case ST_DEVICENAME: ret = ad->upsdev; break; case ST_DEVICEDESCR: ret = "APC Smart UPS\n" " (via serial port - NOT USB!). \n" " Works with higher-end APC UPSes, like\n" " Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.\n" " (Smart-UPS may have to be >= Smart-UPS 700?).\n" " See http://www.networkupstools.org/protocols/apcsmart.html\n" " for protocol compatibility details."; break; case ST_DEVICEURL: ret = "http://www.apc.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = apcsmartXML; break; default: ret = NULL; break; } return (ret); } /* * APC Stonith destructor... */ static void apcsmart_destroy(StonithPlugin * s) { struct pluginDevice *ad = (struct pluginDevice *) s; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); if (ad->upsfd >= 0 && ad->upsdev) { APC_deinit( ad ); } ad->pluginid = NOTpluginID; if (ad->hostlist) { stonith_free_hostlist(ad->hostlist); ad->hostlist = NULL; } if (ad->upsdev != NULL) { FREE(ad->upsdev); ad->upsdev = NULL; } ad->hostcount = -1; ad->upsfd = -1; FREE(ad); } /* * Create a new APC Stonith device. Too bad this function can't be * static */ static StonithPlugin * apcsmart_new(const char *subplugin) { struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (ad == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); return (NULL); } memset(ad, 0, sizeof(*ad)); ad->pluginid = pluginid; ad->hostlist = NULL; ad->hostcount = -1; ad->upsfd = -1; ad->idinfo = DEVICE; ad->sp.s_ops = &apcsmartOps; if (Debug) { LOG(PIL_DEBUG, "%s: returning successfully.", __FUNCTION__); } return &(ad->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/apcsmart.cfg.example0000644000000000000000000000002212120057602031117 0ustar00usergroup00000000000000/dev/ups hostname Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/baytech.c0000644000000000000000000005151712120057602026774 0ustar00usergroup00000000000000/* * Stonith module for BayTech Remote Power Controllers (RPC-x devices) * * Copyright (c) 2000 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "BayTech power switch" #define DOESNT_USE_STONITHKILLCOMM 1 #include "stonith_plugin_common.h" #define PIL_PLUGIN baytech #define PIL_PLUGIN_S "baytech" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" static StonithPlugin * baytech_new(const char *); static void baytech_destroy(StonithPlugin *); static int baytech_set_config(StonithPlugin *, StonithNVpair *); static const char * const * baytech_get_confignames(StonithPlugin * s); static const char * baytech_get_info(StonithPlugin * s, int InfoType); static int baytech_status(StonithPlugin *); static int baytech_reset_req(StonithPlugin * s, int request, const char * host); static char ** baytech_hostlist(StonithPlugin *); static struct stonith_ops baytechOps ={ baytech_new, /* Create new STONITH object */ baytech_destroy, /* Destroy STONITH object */ baytech_get_info, /* Return STONITH info string */ baytech_get_confignames, /* Return STONITH config vars */ baytech_set_config, /* set configuration from vars */ baytech_status, /* Return STONITH device status */ baytech_reset_req, /* Request a reset */ baytech_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_expect_helpers.h" #define MAXOUTLET 32 PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &baytechOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * I have an RPC-5. This code has been tested with this switch. * * The BayTech switches are quite nice, but the dialogues are a bit of a * pain for mechanical parsing. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; char * idinfo; char * unitid; const struct BayTechModelInfo* modelinfo; pid_t pid; int rdfd; int wrfd; char * device; char * user; char * passwd; }; struct BayTechModelInfo { const char * type; /* Baytech model info */ size_t socklen; /* Length of socket name string */ struct Etoken * expect; /* Expect string before outlet list */ }; static int parse_socket_line(struct pluginDevice*,const char * , int *, char *); static const char * pluginid = "BayTech-Stonith"; static const char * NOTpluginID = "BayTech device has been destroyed"; /* * Different expect strings that we get from the Baytech * Remote Power Controllers... */ #define BAYTECHASSOC "Bay Technical Associates" static struct Etoken BayTechAssoc[] = { {BAYTECHASSOC, 0, 0}, {NULL,0,0}}; static struct Etoken UnitId[] = { {"Unit ID: ", 0, 0}, {NULL,0,0}}; static struct Etoken login[] = { {"username>", 0, 0} ,{NULL,0,0}}; static struct Etoken password[] = { {"password>", 0, 0} , {"username>", 0, 0} ,{NULL,0,0}}; static struct Etoken Selection[] = { {"election>", 0, 0} ,{NULL,0,0}}; static struct Etoken RPC[] = { {"RPC", 0, 0} ,{NULL,0,0}}; static struct Etoken LoginOK[] = { {"RPC", 0, 0}, {"Invalid password", 1, 0} , {NULL,0,0}}; static struct Etoken GTSign[] = { {">", 0, 0} ,{NULL,0,0}}; static struct Etoken Menu[] = { {"Menu:", 0, 0} ,{NULL,0,0}}; static struct Etoken Temp[] = { {"emperature: ", 0, 0} , {NULL,0,0}}; static struct Etoken Break[] = { {"Status", 0, 0} , {NULL,0,0}}; static struct Etoken PowerApplied[] = { {"ower applied to outlet", 0, 0} , {NULL,0,0}}; /* We may get a notice about rebooting, or a request for confirmation */ static struct Etoken Rebooting[] = { {"ebooting selected outlet", 0, 0} , {"(Y/N)>", 1, 0} , {"already off.", 2, 0} , {NULL,0,0}}; static struct Etoken TurningOnOff[] = { {"RPC", 0, 0} , {"(Y/N)>", 1, 0} , {"already ", 2, 0} , {NULL,0,0}}; static struct BayTechModelInfo ModelInfo [] = { {"BayTech RPC-5", 18, Temp},/* This first model will be the default */ {"BayTech RPC-3", 10, Break}, {"BayTech RPC-3A", 10, Break}, {NULL, 0, NULL}, }; #include "stonith_config_xml.h" static const char *baytechXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_LOGIN_PARM XML_PASSWD_PARM XML_PARAMETERS_END; static int RPC_connect_device(struct pluginDevice * bt); static int RPCLogin(struct pluginDevice * bt); static int RPCRobustLogin(struct pluginDevice * bt); static int RPCNametoOutletList(struct pluginDevice*, const char * name , int outletlist[]); static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid); static int RPCLogout(struct pluginDevice * bt); static int RPC_onoff(struct pluginDevice*, int unitnum, const char * unitid , int request); /* Login to the Baytech Remote Power Controller (RPC) */ static int RPCLogin(struct pluginDevice * bt) { char IDinfo[128]; static char IDbuf[128]; char * idptr = IDinfo; char * delim; int j; EXPECT(bt->rdfd, RPC, 10); /* Look for the unit type info */ if (EXPECT_TOK(bt->rdfd, BayTechAssoc, 2, IDinfo , sizeof(IDinfo), Debug) < 0) { LOG(PIL_CRIT, "No initial response from %s.", bt->idinfo); return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } idptr += strspn(idptr, WHITESPACE); /* * We should be looking at something like this: * RPC-5 Telnet Host * Revision F 4.22, (C) 1999 * Bay Technical Associates */ /* Truncate the result after the RPC-5 part */ if ((delim = strchr(idptr, ' ')) != NULL) { *delim = EOS; } snprintf(IDbuf, sizeof(IDbuf), "BayTech RPC%s", idptr); REPLSTR(bt->idinfo, IDbuf); if (bt->idinfo == NULL) { return(S_OOPS); } bt->modelinfo = &ModelInfo[0]; for (j=0; ModelInfo[j].type != NULL; ++j) { /* * TIMXXX - * Look at device ID as this really describes the model. */ if (strcasecmp(ModelInfo[j].type, IDbuf) == 0) { bt->modelinfo = &ModelInfo[j]; break; } } /* Look for the unit id info */ EXPECT(bt->rdfd, UnitId, 10); SNARF(bt->rdfd, IDbuf, 2); delim = IDbuf + strcspn(IDbuf, WHITESPACE); *delim = EOS; REPLSTR(bt->unitid, IDbuf); if (bt->unitid == NULL) { return(S_OOPS); } /* Expect "username>" */ EXPECT(bt->rdfd, login, 2); SEND(bt->wrfd, bt->user); SEND(bt->wrfd, "\r"); /* Expect "password>" */ switch (StonithLookFor(bt->rdfd, password, 5)) { case 0: /* Good! */ break; case 1: /* OOPS! got another username prompt */ LOG(PIL_CRIT, "Invalid username for %s.", bt->idinfo); return(S_ACCESS); default: return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } SEND(bt->wrfd, bt->passwd); SEND(bt->wrfd, "\r"); /* Expect "RPC-x Menu" */ switch (StonithLookFor(bt->rdfd, LoginOK, 5)) { case 0: /* Good! */ break; case 1: /* Uh-oh - bad password */ LOG(PIL_CRIT, "Invalid password for %s.", bt->idinfo); return(S_ACCESS); default: return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } EXPECT(bt->rdfd, Menu, 2); return(S_OK); } static int RPCRobustLogin(struct pluginDevice * bt) { int rc=S_OOPS; int j; for (j=0; j < 20 && rc != S_OK; ++j) { if (RPC_connect_device(bt) != S_OK) { continue; } rc = RPCLogin(bt); } return rc; } /* Log out of the Baytech RPC */ static int RPCLogout(struct pluginDevice* bt) { int rc; /* Make sure we're in the right menu... */ SEND(bt->wrfd, "\r"); /* Expect "Selection>" */ rc = StonithLookFor(bt->rdfd, Selection, 5); /* Option 6 is Logout */ SEND(bt->wrfd, "6\r"); close(bt->wrfd); close(bt->rdfd); bt->wrfd = bt->rdfd = -1; return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS)); } /* Reset (power-cycle) the given outlet number */ static int RPCReset(struct pluginDevice* bt, int unitnum, const char * rebootid) { char unum[32]; SEND(bt->wrfd, "\r"); /* Make sure we're in the top level menu */ /* Expect "RPC-x Menu" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND(bt->wrfd, "1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, GTSign, 5); /* Send REBOOT command for given outlet */ snprintf(unum, sizeof(unum), "REBOOT %d\r", unitnum); SEND(bt->wrfd, unum); /* Expect "ebooting "... or "(Y/N)" (if confirmation turned on) */ retry: switch (StonithLookFor(bt->rdfd, Rebooting, 5)) { case 0: /* Got "Rebooting" Do nothing */ break; case 1: /* Got that annoying command confirmation :-( */ SEND(bt->wrfd, "Y\r"); goto retry; case 2: /* Outlet is turned off */ LOG(PIL_CRIT, "Host is OFF: %s.", rebootid); return(S_ISOFF); default: return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } LOG(PIL_INFO, "Host %s (outlet %d) being rebooted." , rebootid, unitnum); /* Expect "ower applied to outlet" */ if (StonithLookFor(bt->rdfd, PowerApplied, 30) < 0) { return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } /* All Right! Power is back on. Life is Good! */ LOG(PIL_INFO, "Power restored to host %s (outlet %d)." , rebootid, unitnum); /* Expect: "RPC-x>" */ EXPECT(bt->rdfd, RPC,5); EXPECT(bt->rdfd, GTSign, 5); /* Pop back to main menu */ SEND(bt->wrfd, "MENU\r"); return(S_OK); } static int RPC_onoff(struct pluginDevice* bt, int unitnum, const char * unitid, int req) { char unum[32]; const char * onoff = (req == ST_POWERON ? "on" : "off"); int rc; if ((rc = RPCRobustLogin(bt) != S_OK)) { LOG(PIL_CRIT, "Cannot log into %s." , bt->idinfo ? bt->idinfo : DEVICE); return(rc); } SEND(bt->wrfd, "\r"); /* Make sure we're in the top level menu */ /* Expect "RPC-x Menu" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND(bt->wrfd, "1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, GTSign, 5); /* Send ON/OFF command for given outlet */ snprintf(unum, sizeof(unum), "%s %d\r" , onoff, unitnum); SEND(bt->wrfd, unum); /* Expect "RPC->x "... or "(Y/N)" (if confirmation turned on) */ if (StonithLookFor(bt->rdfd, TurningOnOff, 10) == 1) { /* They've turned on that annoying command confirmation :-( */ SEND(bt->wrfd, "Y\r"); EXPECT(bt->rdfd, TurningOnOff, 10); } EXPECT(bt->rdfd, GTSign, 10); /* All Right! Command done. Life is Good! */ LOG(PIL_INFO, "Power to host %s (outlet %d) turned %s." , unitid, unitnum, onoff); /* Pop back to main menu */ SEND(bt->wrfd, "MENU\r"); return(S_OK); } /* * Map the given host name into an (AC) Outlet number on the power strip */ static int RPCNametoOutletList(struct pluginDevice* bt, const char * name , int outletlist[]) { char NameMapping[128]; int sockno; char sockname[32]; int maxfound = 0; /* Verify that we're in the top-level menu */ SEND(bt->wrfd, "\r"); /* Expect "RPC-x Menu" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND(bt->wrfd, "1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, GTSign, 5); /* The status command output contains mapping of hosts to outlets */ SEND(bt->wrfd, "STATUS\r"); /* Expect: "emperature:" so we can skip over it... */ EXPECT(bt->rdfd, bt->modelinfo->expect, 5); EXPECT(bt->rdfd, CRNL, 5); /* Looks Good! Parse the status output */ do { char * last; NameMapping[0] = EOS; SNARF(bt->rdfd, NameMapping, 5); if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) { continue; } last = sockname+bt->modelinfo->socklen; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (strcasecmp(name, sockname) == 0) { outletlist[maxfound] = sockno; ++maxfound; } } while (strlen(NameMapping) > 2 && maxfound < MAXOUTLET); /* Pop back out to the top level menu */ SEND(bt->wrfd, "MENU\r"); return(maxfound); } static int baytech_status(StonithPlugin *s) { struct pluginDevice* bt; int rc; ERRIFNOTCONFIGED(s,S_OOPS); bt = (struct pluginDevice*) s; if ((rc = RPCRobustLogin(bt) != S_OK)) { LOG(PIL_CRIT, "Cannot log into %s." , bt->idinfo ? bt->idinfo : DEVICE); return(rc); } /* Verify that we're in the top-level menu */ SEND(bt->wrfd, "\r"); /* Expect "RPC-x Menu" */ EXPECT(bt->rdfd, RPC, 5); EXPECT(bt->rdfd, Menu, 5); return(RPCLogout(bt)); } /* * Return the list of hosts (outlet names) for the devices on this BayTech unit */ static char ** baytech_hostlist(StonithPlugin *s) { char NameMapping[128]; char* NameList[64]; unsigned int numnames = 0; char ** ret = NULL; struct pluginDevice* bt; unsigned int i; ERRIFNOTCONFIGED(s,NULL); bt = (struct pluginDevice*) s; if (RPCRobustLogin(bt) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s." , bt->idinfo ? bt->idinfo : DEVICE); return(NULL); } /* Verify that we're in the top-level menu */ SEND(bt->wrfd, "\r"); /* Expect "RPC-x Menu" */ NULLEXPECT(bt->rdfd, RPC, 5); NULLEXPECT(bt->rdfd, Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND(bt->wrfd, "1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ NULLEXPECT(bt->rdfd, RPC, 5); NULLEXPECT(bt->rdfd, GTSign, 5); /* The status command output contains mapping of hosts to outlets */ SEND(bt->wrfd, "STATUS\r"); /* Expect: "emperature:" so we can skip over it... */ NULLEXPECT(bt->rdfd, bt->modelinfo->expect, 5); NULLEXPECT(bt->rdfd, CRNL, 5); /* Looks Good! Parse the status output */ do { int sockno; char sockname[64]; char * last; char * nm; NameMapping[0] = EOS; NULLSNARF(bt->rdfd, NameMapping, 5); if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) { continue; } last = sockname+bt->modelinfo->socklen; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (numnames >= DIMOF(NameList)-1) { break; } if ((nm = (char*)STRDUP(sockname)) == NULL) { goto out_of_memory; } g_strdown(nm); NameList[numnames] = nm; ++numnames; NameList[numnames] = NULL; } while (strlen(NameMapping) > 2); /* Pop back out to the top level menu */ SEND(bt->wrfd, "MENU\r"); if (numnames >= 1) { ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { goto out_of_memory; }else{ memcpy(ret, NameList, (numnames+1)*sizeof(char*)); } } (void)RPCLogout(bt); return(ret); out_of_memory: LOG(PIL_CRIT, "out of memory"); for (i=0; iOpenStreamSocket(bt->device , TELNET_PORT, TELNET_SERVICE); if (fd < 0) { return(S_OOPS); } bt->rdfd = bt->wrfd = fd; return(S_OK); } /* * Reset the given host on this Stonith device. */ static int baytech_reset_req(StonithPlugin * s, int request, const char * host) { int rc = S_OK; int lorc = 0; struct pluginDevice* bt; ERRIFNOTCONFIGED(s,S_OOPS); bt = (struct pluginDevice*) s; if ((rc = RPCRobustLogin(bt)) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s." , bt->idinfo ? bt->idinfo : DEVICE); }else{ int noutlets; int outlets[MAXOUTLET]; int j; noutlets = RPCNametoOutletList(bt, host, outlets); if (noutlets < 1) { LOG(PIL_CRIT, "%s %s doesn't control host [%s]" , bt->idinfo, bt->unitid, host); return(S_BADHOST); } switch(request) { case ST_POWERON: case ST_POWEROFF: for (j=0; rc == S_OK && j < noutlets;++j) { rc = RPC_onoff(bt, outlets[j], host, request); } break; case ST_GENERIC_RESET: /* * Our strategy here: * 1. Power off all outlets except the last one * 2. reset the last outlet * 3. power the other outlets back on */ for (j=0; rc == S_OK && j < noutlets-1; ++j) { rc = RPC_onoff(bt,outlets[j],host , ST_POWEROFF); } if (rc == S_OK) { rc = RPCReset(bt, outlets[j], host); } for (j=0; rc == S_OK && j < noutlets-1; ++j) { rc = RPC_onoff(bt, outlets[j], host , ST_POWERON); } break; default: rc = S_INVAL; break; } } lorc = RPCLogout(bt); return(rc != S_OK ? rc : lorc); } static const char * const * baytech_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL}; return ret; } /* * Parse the config information in the given string, and stash it away... */ static int baytech_set_config(StonithPlugin* s, StonithNVpair* list) { struct pluginDevice* bt = (struct pluginDevice *)s; int rc; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {ST_LOGIN, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s, S_OOPS); if (bt->sp.isconfigured) { return S_OOPS; } if ((rc =OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } bt->device = namestocopy[0].s_value; bt->user = namestocopy[1].s_value; bt->passwd = namestocopy[2].s_value; return(S_OK); } static const char * baytech_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice* bt; const char * ret; ERRIFWRONGDEV(s,NULL); bt = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: /* What type of device? */ ret = bt->idinfo; break; case ST_DEVICENAME: /* Which particular device? */ ret = bt->device; break; case ST_DEVICEDESCR: /* Description of dev type */ ret = "Bay Technical Associates (Baytech) RPC " "series power switches (via telnet).\n" "The RPC-5, RPC-3 and RPC-3A switches are well tested" "."; break; case ST_DEVICEURL: /* Manufacturer's web site */ ret = "http://www.baytech.net/"; break; case ST_CONF_XML: /* XML metadata */ ret = baytechXML; break; default: ret = NULL; break; } return ret; } /* * Baytech Stonith destructor... */ static void baytech_destroy(StonithPlugin *s) { struct pluginDevice* bt; VOIDERRIFWRONGDEV(s); bt = (struct pluginDevice *)s; bt->pluginid = NOTpluginID; if (bt->rdfd >= 0) { close(bt->rdfd); bt->rdfd = -1; } if (bt->wrfd >= 0) { close(bt->wrfd); bt->wrfd = -1; } if (bt->device != NULL) { FREE(bt->device); bt->device = NULL; } if (bt->user != NULL) { FREE(bt->user); bt->user = NULL; } if (bt->passwd != NULL) { FREE(bt->passwd); bt->passwd = NULL; } if (bt->idinfo != NULL) { FREE(bt->idinfo); bt->idinfo = NULL; } if (bt->unitid != NULL) { FREE(bt->unitid); bt->unitid = NULL; } FREE(bt); } /* Create a new BayTech Stonith device. */ static StonithPlugin * baytech_new(const char *subplugin) { struct pluginDevice* bt = ST_MALLOCT(struct pluginDevice); if (bt == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(bt, 0, sizeof(*bt)); bt->pluginid = pluginid; bt->pid = -1; bt->rdfd = -1; bt->wrfd = -1; REPLSTR(bt->idinfo, DEVICE); if (bt->idinfo == NULL) { FREE(bt); return(NULL); } bt->modelinfo = &ModelInfo[0]; bt->sp.s_ops = &baytechOps; return &(bt->sp); /* same as "bt" */ } static int parse_socket_line(struct pluginDevice * bt, const char *NameMapping , int *sockno, char *sockname) { #if 0 char format[64]; snprintf(format, sizeof(format), "%%7d %%%dc" , bt->modelinfo->socklen); /* 7 digits, 7 blanks, then 'socklen' characters */ /* [0-6]: digits, NameMapping[13] begins the sockname */ /* NameMapping strlen must be >= socklen + 14 */ if (sscanf(NameMapping, format, sockno, sockname) != 2) { return FALSE; } #else # define OFFSET 14 if (sscanf(NameMapping, "%7d", sockno) != 1 || strlen(NameMapping) < OFFSET+bt->modelinfo->socklen) { return FALSE; } strncpy(sockname, NameMapping+OFFSET, bt->modelinfo->socklen); sockname[bt->modelinfo->socklen] = EOS; #endif return TRUE; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/bladehpi.c0000644000000000000000000006450412120057602027125 0ustar00usergroup00000000000000/* * Stonith module for BladeCenter via OpenHPI, an implementation of Service * Availability Forum's Hardware Platfrom Interface * * Author: Dave Blaschke * * Copyright (c) 2005 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "IBM BladeCenter (OpenHPI)" #include "stonith_plugin_common.h" #define PIL_PLUGIN bladehpi #define PIL_PLUGIN_S "bladehpi" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include /* Maximum number of seconds to wait for host to power off */ #define MAX_POWEROFF_WAIT 60 /* entity_root, the one required plugin parameter */ #define ST_ENTITYROOT "entity_root" /* String format of entity_root */ #define SYSTEM_CHASSIS_FMT "{SYSTEM_CHASSIS,%d}" /* soft_reset, the one optional plugin parameter */ #define ST_SOFTRESET "soft_reset" #define OPENHPIURL "http://www.openhpi.org/" /* OpenHPI resource types of interest to this plugin */ #define OHRES_NONE 0 #define OHRES_BLADECENT 1 #define OHRES_MGMTMOD 2 #define OHRES_BLADE 3 /* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that saHpiResourcePowerStateSet can return before the desired state has been achieved by the blade. In the SAHPI_POWER_OFF case this is not good, as whoever calls this plugin assumes that the power is actually off when the plugin returns with a successful return code. Define this constant to build code that loops in one second intervals after calling saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is really off. #define IBMBC_WAIT_FOR_OFF */ static StonithPlugin * bladehpi_new(const char *); static void bladehpi_destroy(StonithPlugin *); static const char * bladehpi_getinfo(StonithPlugin *, int); static const char * const * bladehpi_get_confignames(StonithPlugin *); static int bladehpi_status(StonithPlugin *); static int bladehpi_reset_req(StonithPlugin *, int, const char *); static char ** bladehpi_hostlist(StonithPlugin *); static int bladehpi_set_config(StonithPlugin *, StonithNVpair *); static struct stonith_ops bladehpiOps = { bladehpi_new, /* Create new STONITH object */ bladehpi_destroy, /* Destroy STONITH object */ bladehpi_getinfo, /* Return STONITH info string */ bladehpi_get_confignames, /* Return configuration parameters */ bladehpi_set_config, /* Set configuration */ bladehpi_status, /* Return STONITH device status */ bladehpi_reset_req, /* Request a reset */ bladehpi_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports * PluginImports; static PILPlugin * OurPlugin; static PILInterface * OurInterface; static StonithImports * OurImports; static void * interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us , PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &bladehpiOps , NULL /* close */ , &OurInterface , (void *)&OurImports , &interfprivate); } struct pluginDevice { StonithPlugin sp; const char * pluginid; char * idinfo; char * device; int softreset; GList * hostlist; SaHpiVersionT ohver; /* OpenHPI interface version */ SaHpiSessionIdT ohsession; /* session ID */ SaHpiUint32T ohrptcnt; /* RPT count for hostlist */ SaHpiResourceIdT ohdevid; /* device resource ID */ SaHpiResourceIdT ohsensid; /* sensor resource ID */ SaHpiSensorNumT ohsensnum; /* sensor number */ }; static int open_hpi_session(struct pluginDevice *dev); static void close_hpi_session(struct pluginDevice *dev); static const char *pluginid = "BladeCenterDevice-Stonith"; static const char *NOTpluginID = "IBM BladeCenter device has been destroyed"; #include "stonith_config_xml.h" #define XML_ENTITYROOT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_ENTITYROOT \ XML_PARM_SHORTDESC_END #define XML_ENTITYROOT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The entity_root of the STONITH device from the OpenHPI config file" \ XML_PARM_LONGDESC_END #define XML_ENTITYROOT_PARM \ XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1") \ XML_ENTITYROOT_SHORTDESC \ XML_ENTITYROOT_LONGDESC \ XML_PARAMETER_END #define XML_SOFTRESET_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_SOFTRESET \ XML_PARM_SHORTDESC_END #define XML_SOFTRESET_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \ XML_PARM_LONGDESC_END #define XML_SOFTRESET_PARM \ XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0") \ XML_SOFTRESET_SHORTDESC \ XML_SOFTRESET_LONGDESC \ XML_PARAMETER_END static const char *bladehpiXML = XML_PARAMETERS_BEGIN XML_ENTITYROOT_PARM XML_SOFTRESET_PARM XML_PARAMETERS_END; static int get_resource_type(char *, SaHpiRptEntryT *); static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT); static int get_bladehpi_hostlist(struct pluginDevice *); static void free_bladehpi_hostlist(struct pluginDevice *); static int get_num_tokens(char *str); struct blade_info { char * name; /* blade name */ SaHpiResourceIdT resourceId; /* blade resource ID */ SaHpiCapabilitiesT resourceCaps; /* blade capabilities */ }; static int bladehpi_status(StonithPlugin *s) { struct pluginDevice * dev; SaErrorT ohrc; SaHpiDomainInfoT ohdi; int rc = S_OK; if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } ERRIFWRONGDEV(s, S_OOPS); dev = (struct pluginDevice *)s; rc = open_hpi_session(dev); if( rc != S_OK ) return rc; /* Refresh the hostlist only if RPTs updated */ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get domain info in %s (%d)" , __FUNCTION__, ohrc); rc = S_BADCONFIG; goto done; } if (dev->ohrptcnt != ohdi.RptUpdateCount) { free_bladehpi_hostlist(dev); if (get_bladehpi_hostlist(dev) != S_OK) { LOG(PIL_CRIT, "Unable to obtain list of hosts in %s" , __FUNCTION__); rc = S_BADCONFIG; goto done; } } /* At this point, hostlist is up to date */ if (dev->ohsensid && dev->ohsensnum) { /* * For accurate status, need to make a call that goes out to * BladeCenter MM because the calls made so far by this * function (and perhaps get_bladehpi_hostlist) only retrieve * information from memory cached by OpenHPI */ ohrc = saHpiSensorReadingGet(dev->ohsession , dev->ohsensid, dev->ohsensnum, NULL, NULL); if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) { LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s" , __FUNCTION__); rc = S_OOPS; goto done; } } done: close_hpi_session(dev); return (rc == S_OK) ? (dev->ohdevid ? S_OK : S_OOPS) : rc; } /* * Return the list of hosts configured for this HMC device */ static char ** bladehpi_hostlist(StonithPlugin *s) { struct pluginDevice * dev; int numnames = 0, j; char ** ret = NULL; GList * node = NULL; SaErrorT ohrc; SaHpiDomainInfoT ohdi; int rc = S_OK; if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } ERRIFWRONGDEV(s, NULL); dev = (struct pluginDevice *)s; rc = open_hpi_session(dev); if( rc != S_OK ) return NULL; /* Refresh the hostlist only if RPTs updated */ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get domain info in %s (%d)" , __FUNCTION__, ohrc); goto done; } if (dev->ohrptcnt != ohdi.RptUpdateCount) { free_bladehpi_hostlist(dev); if (get_bladehpi_hostlist(dev) != S_OK) { LOG(PIL_CRIT, "Unable to obtain list of hosts in %s" , __FUNCTION__); goto done; } } /* At this point, hostlist is up to date */ numnames = g_list_length(dev->hostlist); if (numnames < 0) { LOG(PIL_CRIT, "Unconfigured stonith object in %s" , __FUNCTION__); goto done; } ret = (char **)MALLOC((numnames+1) * sizeof(char *)); if (ret == NULL) { LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__); goto done; } memset(ret, 0, (numnames+1) * sizeof(char *)); for (node = g_list_first(dev->hostlist), j = 0 ; NULL != node ; j++, node = g_list_next(node)) { ret[j] = STRDUP(((struct blade_info *)node->data)->name); if (ret[j] == NULL) { LOG(PIL_CRIT, "Out of memory for strdup in %s" , __FUNCTION__); stonith_free_hostlist(ret); ret = NULL; goto done; } g_strdown(ret[j]); } done: close_hpi_session(dev); return ret; } static const char * const * bladehpi_get_confignames(StonithPlugin *s) { static const char * names[] = {ST_ENTITYROOT, NULL}; if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } return names; } /* * Reset the given host, and obey the request type. */ static int bladehpi_reset_req(StonithPlugin *s, int request, const char *host) { GList * node = NULL; struct pluginDevice * dev = NULL; struct blade_info * bi = NULL; SaHpiPowerStateT ohcurstate, ohnewstate; SaHpiDomainInfoT ohdi; SaErrorT ohrc; int rc = S_OK; if (Debug) { LOG(PIL_DEBUG, "%s: called, request=%d, host=%s" , __FUNCTION__, request, host); } ERRIFWRONGDEV(s, S_OOPS); if (host == NULL) { LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__); rc = S_OOPS; goto done; } dev = (struct pluginDevice *)s; rc = open_hpi_session(dev); if( rc != S_OK ) return rc; ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get domain info in %s (%d)" , __FUNCTION__, ohrc); rc = S_BADCONFIG; goto done; } if (dev->ohrptcnt != ohdi.RptUpdateCount) { free_bladehpi_hostlist(dev); if (get_bladehpi_hostlist(dev) != S_OK) { LOG(PIL_CRIT, "Unable to obtain list of hosts in %s" , __FUNCTION__); rc = S_OOPS; goto done; } } for (node = g_list_first(dev->hostlist) ; node != NULL ; node = g_list_next(node)) { bi = ((struct blade_info *)node->data); if (Debug) { LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name); } if (!strcasecmp(bi->name, host)) { break; } } if (!node || !bi) { LOG(PIL_CRIT , "Host %s is not configured in this STONITH module, " "please check your configuration information", host); rc = S_OOPS; goto done; } /* Make sure host has proper capabilities for get */ if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) { LOG(PIL_CRIT , "Host %s does not have power capability", host); rc = S_OOPS; goto done; } ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId , &ohcurstate); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get host %s power state (%d)" , host, ohrc); rc = S_OOPS; goto done; } switch (request) { case ST_POWERON: if (ohcurstate == SAHPI_POWER_ON) { LOG(PIL_INFO, "Host %s already on", host); goto done; } ohnewstate = SAHPI_POWER_ON; break; case ST_POWEROFF: if (ohcurstate == SAHPI_POWER_OFF) { LOG(PIL_INFO, "Host %s already off", host); goto done; } ohnewstate = SAHPI_POWER_OFF; break; case ST_GENERIC_RESET: if (ohcurstate == SAHPI_POWER_OFF) { ohnewstate = SAHPI_POWER_ON; } else { ohnewstate = SAHPI_POWER_CYCLE; } break; default: LOG(PIL_CRIT, "Invalid request argument to %s" , __FUNCTION__); rc = S_INVAL; goto done; } if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) { int maxwait; ohrc = saHpiResourcePowerStateSet(dev->ohsession , bi->resourceId, SAHPI_POWER_OFF); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to set host %s power state to" " OFF (%d)", host, ohrc); rc = S_OOPS; goto done; } /* * Must wait for power off here or subsequent power on request * may take place while power is still on and thus ignored */ maxwait = MAX_POWEROFF_WAIT; do { maxwait--; sleep(1); ohrc = saHpiResourcePowerStateGet(dev->ohsession , bi->resourceId, &ohcurstate); } while ((ohrc == SA_OK) && (ohcurstate != SAHPI_POWER_OFF) && (maxwait > 0)); if (Debug) { LOG(PIL_DEBUG, "Waited %d seconds for power off" , MAX_POWEROFF_WAIT - maxwait); } ohrc = saHpiResourcePowerStateSet(dev->ohsession , bi->resourceId, SAHPI_POWER_ON); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to set host %s power state to" " ON (%d)", host, ohrc); rc = S_OOPS; goto done; } } else { /* Make sure host has proper capabilities to reset */ if ((ohnewstate == SAHPI_POWER_CYCLE) && (!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) { LOG(PIL_CRIT , "Host %s does not have reset capability" , host); rc = S_OOPS; goto done; } if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession , bi->resourceId, ohnewstate)) != SA_OK) { LOG(PIL_CRIT, "Unable to set host %s power state (%d)" , host, ohrc); rc = S_OOPS; goto done; } } #ifdef IBMBC_WAIT_FOR_OFF if (ohnewstate == SAHPI_POWER_OFF) { int maxwait = MAX_POWEROFF_WAIT; do { maxwait--; sleep(1); ohrc = saHpiResourcePowerStateGet(dev->ohsession , bi->resourceId, &ohcurstate); } while ((ohrc == SA_OK) && (ohcurstate != SAHPI_POWER_OFF) && (maxwait > 0)); if (Debug) { LOG(PIL_DEBUG, "Waited %d seconds for power off" , MAX_POWEROFF_WAIT - maxwait); } } #endif LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request); done: close_hpi_session(dev); return rc; } /* * Parse the information in the given configuration file, * and stash it away... */ static int bladehpi_set_config(StonithPlugin *s, StonithNVpair *list) { struct pluginDevice * dev = NULL; StonithNamesToGet namestocopy [] = { {ST_ENTITYROOT, NULL} , {NULL, NULL} }; int rc, i; if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } ERRIFWRONGDEV(s, S_OOPS); dev = (struct pluginDevice *)s; if (Debug) { LOG(PIL_DEBUG, "%s conditionally compiled with:" #ifdef IBMBC_WAIT_FOR_OFF " IBMBC_WAIT_FOR_OFF" #endif , dev->pluginid); } if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } if (Debug) { LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT , namestocopy[0].s_value); } if (get_num_tokens(namestocopy[0].s_value) == 1) { /* name=value pairs on command line, look for soft_reset */ const char *softreset = OurImports->GetValue(list, ST_SOFTRESET); if (softreset != NULL) { if (!strcasecmp(softreset, "true") || !strcmp(softreset, "1")) { dev->softreset = 1; } else if (!strcasecmp(softreset, "false") || !strcmp(softreset, "0")) { dev->softreset = 0; } else { LOG(PIL_CRIT, "Invalid %s %s, must be " "true, 1, false or 0" , ST_SOFTRESET, softreset); FREE(namestocopy[0].s_value); return S_OOPS; } } } else { /* -p or -F option with args "entity_root [soft_reset]..." */ char *pch = namestocopy[0].s_value; /* skip over entity_root and null-terminate */ pch += strcspn(pch, WHITESPACE); *pch = EOS; /* skip over white-space up to next token */ pch++; pch += strspn(pch, WHITESPACE); if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) { dev->softreset = 1; } else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) { dev->softreset = 0; } else { LOG(PIL_CRIT, "Invalid %s %s, must be " "true, 1, false or 0" , ST_SOFTRESET, pch); FREE(namestocopy[0].s_value); return S_OOPS; } } dev->device = STRDUP(namestocopy[0].s_value); FREE(namestocopy[0].s_value); if (dev->device == NULL) { LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__); return S_OOPS; } if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) || sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) { LOG(PIL_CRIT, "Invalid %s %s, must be of format %s" , ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT); return S_BADCONFIG; } dev->ohver = saHpiVersionGet(); if (dev->ohver > SAHPI_INTERFACE_VERSION) { LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than " "one used by plugin (%x), incompatibilites may exist" , dev->ohver, SAHPI_INTERFACE_VERSION); return S_BADCONFIG; } return S_OK; } static int open_hpi_session(struct pluginDevice *dev) { SaErrorT ohrc; ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID , &dev->ohsession, NULL); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc); return S_BADCONFIG; } ohrc = saHpiDiscover(dev->ohsession); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc); return S_BADCONFIG; } return S_OK; } static void close_hpi_session(struct pluginDevice *dev) { if (dev && dev->ohsession) { saHpiSessionClose(dev->ohsession); dev->ohsession = 0; } } static const char * bladehpi_getinfo(StonithPlugin *s, int reqtype) { struct pluginDevice * dev; const char * ret; if (Debug) { LOG(PIL_DEBUG, "%s: called, reqtype=%d" , __FUNCTION__, reqtype); } ERRIFWRONGDEV(s, NULL); dev = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = dev->idinfo; break; case ST_DEVICENAME: ret = dev->device; break; case ST_DEVICEDESCR: ret = "IBM BladeCenter via OpenHPI\n" "Use for IBM xSeries systems managed by BladeCenter\n" " Required parameter name " ST_ENTITYROOT " is " "a string (no white-space) of\n" "the format \""SYSTEM_CHASSIS_FMT"\" " "which is entity_root of BladeCenter\n" "from OpenHPI config file, where %d is a positive " "integer\n" " Optional parameter name " ST_SOFTRESET " is " "true|1 if STONITH device should\n" "use soft reset (power cycle) to reset nodes or " "false|0 if device should\n" "use hard reset (power off, wait, power on); " "default is false"; break; case ST_DEVICEURL: ret = OPENHPIURL; break; case ST_CONF_XML: /* XML metadata */ ret = bladehpiXML; break; default: ret = NULL; break; } return ret; } /* * HMC Stonith destructor... */ static void bladehpi_destroy(StonithPlugin *s) { struct pluginDevice * dev; if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } VOIDERRIFWRONGDEV(s); dev = (struct pluginDevice *)s; dev->pluginid = NOTpluginID; if (dev->device) { FREE(dev->device); dev->device = NULL; } if (dev->idinfo) { FREE(dev->idinfo); dev->idinfo = NULL; } free_bladehpi_hostlist(dev); if (dev->ohsession) { saHpiSessionClose(dev->ohsession); dev->ohsession = 0; } FREE(dev); } static StonithPlugin * bladehpi_new(const char *subplugin) { struct pluginDevice * dev = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s: called", __FUNCTION__); } if (dev == NULL) { LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__); return NULL; } memset(dev, 0, sizeof(*dev)); dev->pluginid = pluginid; dev->device = NULL; dev->hostlist = NULL; REPLSTR(dev->idinfo, DEVICE); if (dev->idinfo == NULL) { FREE(dev); return NULL; } dev->sp.s_ops = &bladehpiOps; if (Debug) { LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__); } return ((void *)dev); } static int get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT) { int i, rc = OHRES_NONE; int foundBlade = 0, foundExp = 0, foundMgmt = 0; int foundRoot = 0, foundOther = 0; char rootName[64]; SaHpiEntityPathT * ohep = &ohRPT->ResourceEntity; if (ohep == NULL || entityRoot == NULL) { return 0; } /* First find root of entity path, which is last entity in entry */ for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) { if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) { break; } } /* Then back up through entries looking for specific entity */ for (i--; i >= 0; i--) { switch (ohep->Entry[i].EntityType) { case SAHPI_ENT_SBC_BLADE: foundBlade = 1; break; case SAHPI_ENT_SYS_EXPANSION_BOARD: foundExp = 1; break; case SAHPI_ENT_SYS_MGMNT_MODULE: if (ohep->Entry[i].EntityLocation == 0) { foundMgmt = 1; } break; case SAHPI_ENT_SYSTEM_CHASSIS: snprintf(rootName, sizeof(rootName) , SYSTEM_CHASSIS_FMT , ohep->Entry[i].EntityLocation); if (!strcmp(entityRoot, rootName)) { foundRoot = 1; } break; default: foundOther = 1; break; } } /* We are only interested in specific entities on specific device */ if (foundRoot) { if (foundMgmt && !(foundBlade||foundExp||foundOther)) { rc = OHRES_MGMTMOD; } else if (!(foundMgmt||foundBlade||foundExp||foundOther)) { rc = OHRES_BLADECENT; } else if (foundBlade && !foundExp) { rc = OHRES_BLADE; } } return rc; } static int get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid) { SaErrorT ohrc = SA_OK; SaHpiEntryIdT ohnextid; SaHpiRdrT ohRDR; ohnextid = SAHPI_FIRST_ENTRY; do { ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid , &ohnextid, &ohRDR); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)" , __FUNCTION__, ohrc); } else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) { return ohRDR.RdrTypeUnion.SensorRec.Num; } } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY); return 0; } /* * Get RPT update count * Loop through all RPT entries * If entry is BladeCenter, save resource ID in dev->ohdevid * If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid * and sensor number in dev->ohsensnum * If entry is blade, save blade_info and add to dev->hostlist * Get RPT update count * If RPT update count changed since start of loop, repeat loop * Save RPT update count in dev->ohrptcnt * * Note that not only does this function update hostlist, it also * updates ohrptcnt, ohdevid, ohsensid and ohsensnum. However, with * this logic it does not need to be called again until the RPT update * count changes. */ static int get_bladehpi_hostlist(struct pluginDevice *dev) { struct blade_info * bi; SaErrorT ohrc; SaHpiEntryIdT ohnextid; SaHpiRptEntryT ohRPT; SaHpiDomainInfoT ohdi; SaHpiUint32T ohupdate; if (Debug) { LOG(PIL_DEBUG, "%s: called, dev->device=%s" , __FUNCTION__, dev->device); } if (dev->device == NULL || *dev->device == 0) { LOG(PIL_CRIT, "Unconfigured stonith object in %s" , __FUNCTION__); return S_BADCONFIG; } ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get domain info in %s (%d)" , __FUNCTION__, ohrc); return S_BADCONFIG; } try_again: ohupdate = ohdi.RptUpdateCount; dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0; ohnextid = SAHPI_FIRST_ENTRY; do { char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH]; int blnum; ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid , &ohnextid, &ohRPT); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)" , __FUNCTION__, ohrc); free_bladehpi_hostlist(dev); return S_BADCONFIG; } switch (get_resource_type(dev->device, &ohRPT)) { case OHRES_BLADECENT: dev->ohdevid = ohRPT.ResourceId; if (Debug) { LOG(PIL_DEBUG, "BladeCenter '%s' has id %d" , (char*)ohRPT.ResourceTag.Data , dev->ohdevid); } break; case OHRES_MGMTMOD: if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){ dev->ohsensnum = get_sensor_num(dev->ohsession , ohRPT.ResourceId); if (dev->ohsensnum) { dev->ohsensid = ohRPT.ResourceId; if (Debug) { LOG(PIL_DEBUG , "MgmtModule '%s' has id %d " "with sensor #%d" , (char*)ohRPT.ResourceTag.Data , dev->ohsensid , dev->ohsensnum); } } } break; case OHRES_BLADE: if ((bi = (struct blade_info *) MALLOC(sizeof(struct blade_info))) == NULL) { LOG(PIL_CRIT, "Out of memory in %s" , __FUNCTION__); free_bladehpi_hostlist(dev); return S_OOPS; } /* * New format consists of "Blade N - name" while older * format consists only of "name"; we only need to * stash name because ResourceID is the important info */ if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s" , &blnum, blname) == 2) { bi->name = STRDUP(blname); } else { bi->name = STRDUP((char*)ohRPT.ResourceTag.Data); } if (bi->name == NULL) { LOG(PIL_CRIT, "Out of memory for strdup in %s" , __FUNCTION__); free_bladehpi_hostlist(dev); return S_OOPS; } bi->resourceId = ohRPT.ResourceId; bi->resourceCaps = ohRPT.ResourceCapabilities; dev->hostlist = g_list_append(dev->hostlist, bi); if (Debug) { LOG(PIL_DEBUG, "Blade '%s' has id %d, caps %x" , bi->name, bi->resourceId, bi->resourceCaps); } break; } } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY); ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi); if (ohrc != SA_OK) { LOG(PIL_CRIT, "Unable to get domain info in %s (%d)" , __FUNCTION__, ohrc); free_bladehpi_hostlist(dev); return S_BADCONFIG; } if (ohupdate != ohdi.RptUpdateCount) { free_bladehpi_hostlist(dev); if(Debug){ LOG(PIL_DEBUG, "Looping through entries again," " count changed from %d to %d" , ohupdate, ohdi.RptUpdateCount); } goto try_again; } dev->ohrptcnt = ohupdate; return S_OK; } static void free_bladehpi_hostlist(struct pluginDevice *dev) { if (dev->hostlist) { GList *node; while (NULL != (node = g_list_first(dev->hostlist))) { dev->hostlist = g_list_remove_link(dev->hostlist, node); FREE(((struct blade_info *)node->data)->name); FREE(node->data); g_list_free(node); } dev->hostlist = NULL; } dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0; } static int get_num_tokens(char *str) { int namecount = 0; while (*str != EOS) { str += strspn(str, WHITESPACE); if (*str == EOS) break; str += strcspn(str, WHITESPACE); namecount++; } return namecount; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/cyclades.c0000644000000000000000000003531512120057602027142 0ustar00usergroup00000000000000/* * Stonith module for Cyclades AlterPath PM * Bases off the SSH plugin * * Copyright (c) 2004 Cyclades corp. * * Author: Jon Taylor * * Rewritten from scratch using baytech.c structure and code * and currently maintained by * Marcelo Tosatti * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "Cyclades AlterPath PM" #define DOESNT_USE_STONITHSCANLINE #include "stonith_plugin_common.h" #define PIL_PLUGIN cyclades #define PIL_PLUGIN_S "cyclades" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" static StonithPlugin * cyclades_new(const char *); static void cyclades_destroy(StonithPlugin *); static int cyclades_set_config(StonithPlugin *, StonithNVpair *); static const char * const * cyclades_get_confignames(StonithPlugin * s); static const char * cyclades_get_info(StonithPlugin * s, int InfoType); static int cyclades_status(StonithPlugin *); static int cyclades_reset_req(StonithPlugin * s, int request, const char * host); static char ** cyclades_hostlist(StonithPlugin *); static struct stonith_ops cycladesOps ={ cyclades_new, /* Create new STONITH object */ cyclades_destroy, /* Destroy STONITH object */ cyclades_get_info, /* Return STONITH info string */ cyclades_get_confignames, /* Return STONITH config vars */ cyclades_set_config, /* set configuration from vars */ cyclades_status, /* Return STONITH device status */ cyclades_reset_req, /* Request a reset */ cyclades_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_expect_helpers.h" PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &cycladesOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * Cyclades STONITH device * */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char * device; char * user; int serial_port; /* pid of ssh client process and its in/out file descriptors */ pid_t pid; int rdfd, wrfd; }; static struct Etoken StatusOutput[] = { { "Outlet\t\tName\t\tStatus\t\tUsers\t\tInterval (s)", 1, 0}, { "Outlet\tName\t\t\tStatus\t\tInterval (s)\tUsers", 2, 0}, { "Outlet Name Status Post-on Delay(s)", 3, 0}, { NULL, 0, 0} }; static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}}; /* Commands of PM devices */ static char status_all[] = "status all"; static char cycle[] = "cycle"; static int CYC_robust_cmd(struct pluginDevice *, char *); static const char * pluginid = "CycladesDevice-Stonith"; static const char * NOTpluginID = "Cyclades device has been destroyed"; #define MAX_OUTLETS 128 #define ST_SERIALPORT "serialport" #define ZEROEXPECT(fd,p,t) { \ if (StonithLookFor(fd, p, t) < 0) \ return(0); \ } #define RESETEXPECT(fd,p,t) { \ if (StonithLookFor(fd, p, t) < 0) { \ FREE(outletstr); \ return(errno == ETIMEDOUT \ ? S_RESETFAIL : S_OOPS); \ } \ } #include "stonith_config_xml.h" #define XML_SERIALPORT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_SERIALPORT \ XML_PARM_SHORTDESC_END #define XML_SERIALPORT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The serial port of the IPDU which can powercycle the node" \ XML_PARM_LONGDESC_END #define XML_SERIALPORT_PARM \ XML_PARAMETER_BEGIN(ST_SERIALPORT, "string", "1") \ XML_SERIALPORT_SHORTDESC \ XML_SERIALPORT_LONGDESC \ XML_PARAMETER_END static const char *cycladesXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_LOGIN_PARM XML_SERIALPORT_PARM XML_PARAMETERS_END; static int CYCScanLine(struct pluginDevice *sd, int timeout, char * buf, int max) { if (EXPECT_TOK(sd->rdfd, CRNL, timeout, buf, max, Debug) < 0) { Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid); return(S_OOPS); } return(S_OK); } static int cyclades_status(StonithPlugin *s) { struct pluginDevice *sd; char *cmd = status_all; ERRIFNOTCONFIGED(s,S_OOPS); sd = (struct pluginDevice*) s; if (CYC_robust_cmd(sd, cmd) != S_OK) { LOG(PIL_CRIT, "can't run status all command"); return(S_OOPS); } EXPECT(sd->rdfd, StatusOutput, 50); return(S_OK); } static int CYC_run_command(struct pluginDevice *sd, char *cmd) { char SshCommand[MAX_OUTLETS*4]; snprintf(SshCommand, sizeof(SshCommand), "exec ssh -q %s@%s /bin/pmCommand %d %s 2>/dev/null", sd->user, sd->device, sd->serial_port, cmd); sd->pid = STARTPROC(SshCommand, &sd->rdfd, &sd->wrfd); if (sd->pid <= 0) { return(S_OOPS); } return(S_OK); } static int CYC_robust_cmd(struct pluginDevice *sd, char *cmd) { int rc = S_OOPS; int i; for (i=0; i < 20 && rc != S_OK; i++) { if (sd->pid > 0) { Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid); } if (CYC_run_command(sd, cmd) != S_OK) { Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid); continue; } rc = S_OK; } return rc; } #define MAXSAVE 512 static int CYCNametoOutlet(struct pluginDevice *sd, const char *host, int *outlets, int maxoutlet) { char *cmd = status_all; char savebuf[MAXSAVE]; int err; int outlet, numoutlet = 0; char name[17], locked[11], on[4]; if (CYC_robust_cmd(sd, cmd) != S_OK) { LOG(PIL_CRIT, "can't run status all command"); return 0; } ZEROEXPECT(sd->rdfd, StatusOutput, 50); ZEROEXPECT(sd->rdfd, CRNL, 50); do { memset(savebuf, 0, sizeof(savebuf)); memset(name, 0, sizeof(name)); memset(locked, 0, sizeof(locked)); memset(on, 0, sizeof(on)); err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf)); if ((err == S_OK) && (sscanf(savebuf,"%3d %16s %10s %3s", &outlet, name, locked, on) > 0)) { if (!strncasecmp(name, host, strlen(host))) { if (numoutlet >= maxoutlet) { LOG(PIL_CRIT, "too many outlets"); return 0; } outlets[numoutlet++] = outlet; } } } while (err == S_OK); return (numoutlet); } /* * Return the list of hosts configured for this Cyclades device */ static char ** cyclades_hostlist(StonithPlugin *s) { struct pluginDevice* sd; char *cmd = status_all; char savebuf[MAXSAVE]; int err, i; int outlet; int numnames = 0; char name[17], locked[11], on[4]; char *NameList[MAX_OUTLETS]; char **ret = NULL; ERRIFNOTCONFIGED(s,NULL); sd = (struct pluginDevice*) s; if (CYC_robust_cmd(sd, cmd) != S_OK) { LOG(PIL_CRIT, "can't run status all command"); return (NULL); } memset(savebuf, 0, sizeof(savebuf)); NULLEXPECT(sd->rdfd, StatusOutput, 50); NULLEXPECT(sd->rdfd, CRNL, 50); do { char *nm; memset(savebuf, 0, sizeof(savebuf)); memset(name, 0, sizeof(name)); memset(locked, 0, sizeof(locked)); memset(on, 0, sizeof(on)); err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf)); if ((err == S_OK) && (sscanf(savebuf,"%3d %16s %10s %3s", &outlet, name, locked, on) > 0)) { nm = (char *) STRDUP (name); if (!nm) { goto out_of_memory; } g_strdown(nm); NameList[numnames] = nm; numnames++; NameList[numnames] = NULL; } } while (err == S_OK); if (numnames) { ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { goto out_of_memory; } else { memcpy(ret, NameList, (numnames+1)*sizeof(char*)); } return (ret); } return(ret); out_of_memory: LOG(PIL_CRIT, "out of memory"); for (i=0; irdfd, exp, 50); } LOG(PIL_DEBUG, "Power to host %s turned %s", unitid, onoff); FREE(outletstr); return (S_OK); } static int cyclades_reset(struct pluginDevice *sd, int *outlet, int numoutlet, const char *unitid) { char cmd[MAX_OUTLETS*4], expstring[64]; struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}}; char *outletstr; int i; memset(cmd, 0, sizeof(cmd)); outletstr = cyclades_outletstr(outlet, numoutlet); if (outletstr == NULL) { LOG(PIL_CRIT, "out of memory"); return (S_OOPS); } snprintf(cmd, sizeof(cmd), "%s %s", cycle, outletstr); LOG(PIL_INFO, "Host %s being rebooted.", unitid); if (CYC_robust_cmd(sd, cmd) != S_OK) { LOG(PIL_CRIT, "can't run cycle command"); FREE(outletstr); return(S_OOPS); } for (i = 0; i < numoutlet; i++) { memset(expstring, 0, sizeof(expstring)); snprintf(expstring, sizeof(expstring) , "%d: Outlet turned off.", outlet[i]); exp[0].string = expstring; RESETEXPECT(sd->rdfd, exp, 50); } for (i = 0; i < numoutlet; i++) { memset(expstring, 0, sizeof(expstring)); snprintf(expstring, sizeof(expstring) , "%d: Outlet turned on.", outlet[i]); exp[0].string = expstring; RESETEXPECT(sd->rdfd, exp, 50); } FREE(outletstr); return (S_OK); } /* * Reset the given host on this Stonith device. */ static int cyclades_reset_req(StonithPlugin * s, int request, const char * host) { struct pluginDevice *sd; int rc = 0; int numoutlet, outlets[MAX_OUTLETS]; ERRIFNOTCONFIGED(s,S_OOPS); sd = (struct pluginDevice*) s; numoutlet = CYCNametoOutlet(sd, host, outlets, MAX_OUTLETS); if (!numoutlet) { LOG(PIL_CRIT, "Unknown host %s to Cyclades PM", host); return (S_OOPS); } switch (request) { case ST_POWERON: case ST_POWEROFF: rc = cyclades_onoff(sd, outlets, numoutlet, host, request); break; case ST_GENERIC_RESET: rc = cyclades_reset(sd, outlets, numoutlet, host); break; default: rc = S_INVAL; break; } return rc; } static const char * const * cyclades_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_SERIALPORT, NULL}; return ret; } /* * Parse the config information in the given string, and stash it away... */ static int cyclades_set_config(StonithPlugin* s, StonithNVpair* list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; StonithNamesToGet namestocopy[] = { {ST_IPADDR, NULL} , {ST_LOGIN, NULL} , {ST_SERIALPORT, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s, S_OOPS); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->device = namestocopy[0].s_value; sd->user = namestocopy[1].s_value; sd->serial_port = atoi(namestocopy[2].s_value); FREE(namestocopy[2].s_value); return(S_OK); } static const char * cyclades_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice * sd; const char * ret; ERRIFWRONGDEV(s, NULL); sd = (struct pluginDevice*) s; switch (reqtype) { case ST_DEVICEID: /* What type of device? */ /* FIXME: could inform the exact PM model */ ret = sd->idinfo; break; case ST_DEVICENAME: /* What particular device? */ ret = sd->device; break; case ST_DEVICEDESCR: /* Description of dev type */ ret = "Cyclades AlterPath PM " "series power switches (via TS/ACS/KVM)."; break; case ST_DEVICEURL: /* Manufacturer's web site */ ret = "http://www.cyclades.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = cycladesXML; break; default: ret = NULL; break; } return ret; } /* * Cyclades Stonith destructor... */ static void cyclades_destroy(StonithPlugin *s) { struct pluginDevice* sd; VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice*) s; sd->pluginid = NOTpluginID; Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid); if (sd->device != NULL) { FREE(sd->device); sd->device = NULL; } if (sd->user != NULL) { FREE(sd->user); sd->user = NULL; } FREE(sd); } /* Create a new cyclades Stonith device */ static StonithPlugin * cyclades_new(const char *plugin) { struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice); if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; sd->pid = -1; sd->rdfd = -1; sd->wrfd = -1; sd->idinfo = DEVICE; sd->sp.s_ops = &cycladesOps; return &(sd->sp); /* same as sd */ } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/drac3.c0000644000000000000000000002136312120057602026345 0ustar00usergroup00000000000000/* * Stonith module for Dell DRACIII (Dell Remote Access Card) * * Copyright (C) 2003 Alfa21 Outsourcing * Copyright (C) 2003 Roberto Moreda * Tiny bits Copyright 2005 International Business Machines * Significantly Mangled by Sun Jiang Dong , IBM, 2005 * * (Using snippets of other stonith modules code) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define DEVICE "Dell DRACIII Card" #include "stonith_plugin_common.h" #include #include "drac3_command.h" #define PIL_PLUGIN drac3 #define PIL_PLUGIN_S "drac3" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" static StonithPlugin * drac3_new(const char *); static void drac3_destroy(StonithPlugin *); static const char * const * drac3_get_confignames(StonithPlugin *); static int drac3_set_config(StonithPlugin *, StonithNVpair *); static const char * drac3_getinfo(StonithPlugin * s, int InfoType); static int drac3_status(StonithPlugin * ); static int drac3_reset_req(StonithPlugin * s, int request, const char * host); static char ** drac3_hostlist(StonithPlugin *); static struct stonith_ops drac3Ops ={ drac3_new, /* Create new STONITH object */ drac3_destroy, /* Destroy STONITH object */ drac3_getinfo, /* Return STONITH info string */ drac3_get_confignames, /* Return configuration parameters */ drac3_set_config, /* Set configuration */ drac3_status, /* Return STONITH device status */ drac3_reset_req, /* Request a reset */ drac3_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &drac3Ops , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } #define BUFLEN 1024 #define ST_HOST "host" struct pluginDevice { StonithPlugin sp; const char *pluginid; const char *idinfo; CURL *curl; char *host; char *user; char *pass; }; static const char *pluginid = "Dell-DRACIII-Stonith"; static const char *NOTpluginID = "Dell DRACIII device has been destroyed"; #include "stonith_config_xml.h" #define XML_HOST_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_HOST \ XML_PARM_SHORTDESC_END #define XML_HOST_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The hostname of the STONITH device" \ XML_PARM_LONGDESC_END #define XML_HOST_PARM \ XML_PARAMETER_BEGIN(ST_HOST, "string", "1") \ XML_HOST_SHORTDESC \ XML_HOST_LONGDESC \ XML_PARAMETER_END static const char *drac3XML = XML_PARAMETERS_BEGIN XML_HOST_PARM XML_LOGIN_PARM XML_PASSWD_PARM XML_PARAMETERS_END; /* ------------------------------------------------------------------ */ /* STONITH PLUGIN API */ /* ------------------------------------------------------------------ */ static StonithPlugin * drac3_new(const char *subplugin) { struct pluginDevice *drac3d = ST_MALLOCT(struct pluginDevice); if (drac3d == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(drac3d, 0, sizeof(*drac3d)); drac3d->pluginid = pluginid; drac3d->curl = curl_easy_init(); drac3InitCurl(drac3d->curl); drac3d->host = NULL; drac3d->user = NULL; drac3d->pass = NULL; drac3d->idinfo = DEVICE; drac3d->sp.s_ops = &drac3Ops; return (&(drac3d->sp)); } /* ------------------------------------------------------------------ */ static void drac3_destroy(StonithPlugin * s) { struct pluginDevice *drac3d; VOIDERRIFWRONGDEV(s); drac3d = (struct pluginDevice *) s; drac3d->pluginid = NOTpluginID; /* release curl connection */ if (drac3d->curl != NULL) { drac3Logout(drac3d->curl, drac3d->host); curl_easy_cleanup(drac3d->curl); drac3d->curl = NULL; } if (drac3d->host != NULL) { FREE(drac3d->host); drac3d->host = NULL; } if (drac3d->user != NULL) { FREE(drac3d->user); drac3d->user = NULL; } if (drac3d->pass != NULL) { FREE(drac3d->pass); drac3d->pass = NULL; } /* release stonith-object itself */ FREE(drac3d); } /* ------------------------------------------------------------------ */ static const char * const * drac3_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_HOST, ST_LOGIN, ST_PASSWD, NULL}; return ret; } /* ------------------------------------------------------------------ */ static int drac3_set_config(StonithPlugin * s, StonithNVpair * list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; StonithNamesToGet namestocopy [] = { {ST_HOST, NULL} , {ST_LOGIN, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s, S_OOPS); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->host = namestocopy[0].s_value; sd->user = namestocopy[1].s_value; sd->pass = namestocopy[2].s_value; return(S_OK); } /* ------------------------------------------------------------------ */ const char * drac3_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice *drac3d; const char *ret = NULL; ERRIFWRONGDEV(s,NULL); drac3d = (struct pluginDevice *) s; switch (reqtype) { case ST_DEVICEID: ret = drac3d->idinfo; break; case ST_DEVICENAME: ret = drac3d->host; break; case ST_DEVICEDESCR: ret = "Dell DRACIII (via HTTPS)\n" "The Dell Remote Access Controller accepts XML " "commands over HTTPS"; break; case ST_DEVICEURL: ret = "http://www.dell.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = drac3XML; break; default: ret = NULL; break; } return(ret); } /* ------------------------------------------------------------------ */ int drac3_status(StonithPlugin *s) { struct pluginDevice *drac3d; ERRIFNOTCONFIGED(s,S_OOPS); drac3d = (struct pluginDevice *) s; if (drac3VerifyLogin(drac3d->curl, drac3d->host)) { if (drac3Login(drac3d->curl, drac3d->host, drac3d->user, drac3d->pass)) { LOG(PIL_CRIT, "%s: cannot log into %s at %s", __FUNCTION__, drac3d->idinfo, drac3d->host); return(S_ACCESS); } } if (drac3GetSysInfo(drac3d->curl, drac3d->host)) { return(S_ACCESS); }else{ return(S_OK); } } /* ------------------------------------------------------------------ */ int drac3_reset_req(StonithPlugin * s, int request, const char *host) { struct pluginDevice *drac3d; int rc = S_OK; ERRIFNOTCONFIGED(s,S_OOPS); drac3d = (struct pluginDevice *) s; if (strcasecmp(host, drac3d->host)) { LOG(PIL_CRIT, "%s doesn't control host [%s]" , drac3d->idinfo, host); return(S_BADHOST); } if (drac3VerifyLogin(drac3d->curl, drac3d->host)) { if (drac3Login(drac3d->curl, drac3d->host, drac3d->user, drac3d->pass)) { LOG(PIL_CRIT, "%s: cannot log into %s at %s", __FUNCTION__, drac3d->idinfo, drac3d->host); return(S_ACCESS); } } switch(request) { #if defined(ST_POWERON) && defined(ST_POWEROFF) case ST_POWERON: case ST_POWEROFF: /* TODO... */ #endif case ST_GENERIC_RESET: if (drac3PowerCycle(drac3d->curl, drac3d->host)) rc = S_ACCESS; break; default: rc = S_INVAL; break; } return(rc); } /* ------------------------------------------------------------------ */ char ** drac3_hostlist(StonithPlugin * s) { struct pluginDevice *drac3d; char **hl; ERRIFNOTCONFIGED(s,NULL); drac3d = (struct pluginDevice *) s; hl = OurImports->StringToHostList(drac3d->host); if (hl == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); } else { g_strdown(hl[0]); } return(hl); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/drac3_command.c0000644000000000000000000002203612120057602030041 0ustar00usergroup00000000000000/* * Stonith module for Dell DRACIII (Dell Remote Access Card) * * Copyright (C) 2003 Alfa21 Outsourcing * Copyright (C) 2003 Roberto Moreda * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include "drac3_command.h" #include "drac3_hash.h" #define BUFLEN 1024 /* buffer */ #define SBUFLEN 256 /* small buffer */ #define MD5LEN 16 /* md5 buffer */ #define DEBUG 0 /* Hardcoded XML commands and response codes */ #define CMD_POWERCYCLE "powercycle\n" #define CMD_GETSYSINFO "getsysinfo -A\n" #define RC_OK "0x0\n" struct Chunk { char *memory; size_t size; }; /* prototypes */ int xmlGetXPathString (const char *str, const char * expr, char * rc, const int len); size_t writeFunction (void *ptr, size_t size, size_t nmemb, void *data); /* ---------------------------------------------------------------------- * * XML PARSING * * ---------------------------------------------------------------------- */ int xmlGetXPathString (const char *str, const char * expr, char * rc, const int len) { xmlDocPtr doc; xmlNodePtr cur; xmlXPathContextPtr ctx; xmlXPathObjectPtr path; xmlChar *xmlRC; if (!strchr(str,'<')) { fprintf(stderr,"%s malformed\n", str); rc[0] = 0x00; return(1); } doc = xmlParseMemory(str, strlen(str)); xmlXPathInit(); ctx = xmlXPathNewContext(doc); path = xmlXPathEvalExpression((const xmlChar *)expr, ctx); cur = path->nodesetval->nodeTab[0]; if (cur != NULL) { xmlRC = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); snprintf(rc, len, "%s\n", xmlRC); xmlFree(xmlRC); xmlFreeDoc(doc); xmlCleanupParser(); xmlXPathFreeObject(path); xmlXPathFreeContext(ctx); return(0); } else { fprintf(stderr,"error in obtaining XPath %s\n", expr); xmlFreeDoc(doc); xmlCleanupParser(); xmlXPathFreeObject(path); xmlXPathFreeContext(ctx); rc[0] = 0x00; return(1); } } /* ---------------------------------------------------------------------- * * CURL CALLBACKS * * ---------------------------------------------------------------------- */ size_t writeFunction (void *ptr, size_t size, size_t nmemb, void *data) { register int realsize = size * nmemb; struct Chunk *mem = (struct Chunk *)data; mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); if (mem->memory) { memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; mem->memory[mem->size] = 0; } return realsize; } /* ---------------------------------------------------------------------- * * DRAC3 CURL COMMANDS * * ---------------------------------------------------------------------- */ int drac3InitCurl (CURL *curl) { #ifdef CURLOPT_NOSIGNAL if (curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)) return(1); #endif if (curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30)) return(1); if (curl_easy_setopt(curl, CURLOPT_VERBOSE, 0)) return(1); if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction)) return(1); if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/dev/null")) return(1); if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)) return(1); if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0)) return(1); return(0); } int drac3Login (CURL *curl, const char *host, const char *user, const char *pass) { char url[BUFLEN]; char chall[BUFLEN]; char token[BUFLEN]; char rc[SBUFLEN]; int status; struct Chunk chunk; chunk.memory = NULL; chunk.size = 0; if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1); /* ask for challenge */ snprintf(url, BUFLEN, "https://%s/cgi/challenge", host); url[BUFLEN-1] = 0x00; if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1); if (curl_easy_perform(curl)) return(1); /* extract challenge */ status = xmlGetXPathString(chunk.memory, "//CHALLENGE", chall, BUFLEN); if (status) { free(chunk.memory); return(1); } /* calculate authToken */ drac3AuthHash(chall, pass, token, BUFLEN); if (DEBUG) printf("T: %s\n", token); status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN); if (status) { free(chunk.memory); return(1); } if (DEBUG) printf("RC: %s\n", rc); status = (strcmp(rc, RC_OK) == 0) ? 0 : 1; free(chunk.memory); if (status) return(1); chunk.memory = NULL; chunk.size = 0; /* sends authToken */ snprintf(url, BUFLEN, "https://%s/cgi/login?user=%s&hash=%s", host, user, token); url[BUFLEN-1] = 0x00; if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1); if (curl_easy_perform(curl)) return(1); if (DEBUG) printf("R: %s\n", chunk.memory); status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN); if (status) { free(chunk.memory); return(1); } if (DEBUG) printf("RC: %s\n", rc); status = (strcmp(rc, RC_OK) == 0) ? 0 : 1; free(chunk.memory); return(status); } int drac3PowerCycle (CURL *curl, const char *host) { char url[BUFLEN]; char cmd[]=CMD_POWERCYCLE; char rc[SBUFLEN]; int status; struct Chunk chunk; chunk.memory = NULL; chunk.size = 0; if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1); snprintf(url, BUFLEN, "https://%s/cgi/bin", host); url[BUFLEN-1] = 0x00; if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1); if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1); if (curl_easy_perform(curl)) return(1); if (DEBUG) printf("R: %s\n", chunk.memory); status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN); if (status) { free(chunk.memory); return(1); } if (DEBUG) printf("RC: %s\n", rc); status = (strcmp(rc, RC_OK) == 0) ? 0 : 1; free(chunk.memory); return(status); } int drac3GetSysInfo (CURL *curl, const char *host) { char url[BUFLEN]; char cmd[]=CMD_GETSYSINFO; char rc[SBUFLEN]; int status; struct Chunk chunk; chunk.memory = NULL; chunk.size = 0; if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1); snprintf(url, BUFLEN, "https://%s/cgi/bin", host); url[BUFLEN-1] = 0x00; if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1); if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1); if (curl_easy_perform(curl)) return(1); if (DEBUG) printf("R: %s\n", chunk.memory); status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN); if (status) { free(chunk.memory); return(1); } if (DEBUG) printf("RC: %s\n", rc); status = (strcmp(rc, RC_OK) == 0) ? 0 : 1; free(chunk.memory); return(status); } int drac3Logout (CURL *curl, const char *host) { char url[BUFLEN]; char rc[SBUFLEN]; int status; struct Chunk chunk; chunk.memory = NULL; chunk.size = 0; if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1); snprintf(url, BUFLEN, "https://%s/cgi/logout", host); url[BUFLEN-1] = 0x00; if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1); if (curl_easy_perform(curl)) return(1); if (DEBUG) printf("R: %s\n", chunk.memory); status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN); if (status) { free(chunk.memory); return(1); } if (DEBUG) printf("RC: %s\n", rc); status = (strcmp(rc, RC_OK) == 0) ? 0 : 1; free(chunk.memory); return(status); } int drac3VerifyLogin (CURL *curl, const char *host) { /*We try to do a GetSysInfo */ return(drac3GetSysInfo (curl, host)); } /* -------------------------------------------------------------------- */ Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/drac3_command.h0000644000000000000000000000232212120057602030042 0ustar00usergroup00000000000000/* * Stonith module for Dell DRACIII (Dell Remote Access Card) * * Copyright (C) 2003 Alfa21 Outsourcing * Copyright (C) 2003 Roberto Moreda * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ int drac3InitCurl (CURL *curl); int drac3Login (CURL *curl, const char *host, const char *user, const char *pass); int drac3PowerCycle (CURL *curl, const char *host); int drac3GetSysInfo (CURL *curl, const char *host); int drac3Logout (CURL *curl, const char *host); int drac3VerifyLogin (CURL *curl, const char *host); Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/drac3_hash.c0000644000000000000000000000604712120057602027352 0ustar00usergroup00000000000000/* * Stonith module for Dell DRACIII (Dell Remote Access Card) * * Copyright (C) 2003 Alfa21 Outsourcing * Copyright (C) 2003 Roberto Moreda * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include "drac3_hash.h" #define BUFLEN 1024 /* buffer */ #define SBUFLEN 256 /* small buffer */ #define MD5LEN 16 /* md5 buffer */ /* Hash functions for DRAC3 authentication */ guint16 drac3Crc16(const char *str, const int l) { int i,j; guint16 crc = 0; for (i=0; i challBytes */ memset(challBytes, 0, MD5LEN); chall_dup = g_strdup(chall); if (chall_dup[strlen(chall_dup) - 1] == '\n' ) { chall_dup[strlen(chall_dup) - 1] = '\0'; } base64_to_binary(chall_dup, strlen(chall_dup), challBytes, MD5LEN); /* gets MD5 from pass -> passMD5 */ MD5((const unsigned char *)pass, strlen(pass), (unsigned char *)passMD5); /* calculate challBytes and passMD5 xor -> xorBytes */ for (i=0; i xorBytesMD5 */ MD5((unsigned char *)xorBytes, MD5LEN, (unsigned char *)xorBytesMD5); /* calculate xorBytesMD5 crc16 */ crc = drac3Crc16(xorBytesMD5, MD5LEN); /* joins xorBytesMD5 and crc16 -> response */ memcpy(response, xorBytesMD5, MD5LEN); memcpy(response+MD5LEN, &crc, 2); /* calculate response base64 -> responseb64 */ memset(responseb64, 0, SBUFLEN); binary_to_base64(response, MD5LEN+2, responseb64, SBUFLEN); /* assuring null-termination */ responseb64[SBUFLEN-1]=0x00; snprintf(token, len, "%s", responseb64); token[len-1]=0x00; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/drac3_hash.h0000644000000000000000000000207512120057602027354 0ustar00usergroup00000000000000/* * Stonith module for Dell DRACIII (Dell Remote Access Card) * * Copyright (C) 2003 Alfa21 Outsourcing * Copyright (C) 2003 Roberto Moreda * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include guint16 drac3Crc16(const char *str, const int l); void drac3AuthHash(const char *chall, const char *pass, char *token, int len); Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external.c0000644000000000000000000004652312120057602027200 0ustar00usergroup00000000000000/* * Stonith module for EXTERNAL Stonith device * * Copyright (c) 2001 SuSE Linux AG * Portions Copyright (c) 2004, tummy.com, ltd. * * Based on ssh.c, Authors: Joachim Gleissner , * Lars Marowsky-Bree * Modified for external.c: Scott Kleihege * Reviewed, tested, and config parsing: Sean Reifschneider * And overhauled by Lars Marowsky-Bree , so the circle * closes... * Mangled by Zhaokai , IBM, 2005 * Changed to allow full-featured external plugins by Dave Blaschke * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include "stonith_plugin_common.h" #define PIL_PLUGIN external #define PIL_PLUGIN_S "external" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * external_new(const char *); static void external_destroy(StonithPlugin *); static int external_set_config(StonithPlugin *, StonithNVpair *); static const char * const * external_get_confignames(StonithPlugin *); static const char * external_getinfo(StonithPlugin * s, int InfoType); static int external_status(StonithPlugin * ); static int external_reset_req(StonithPlugin * s, int request, const char * host); static char ** external_hostlist(StonithPlugin *); static struct stonith_ops externalOps ={ external_new, /* Create new STONITH object */ external_destroy, /* Destroy STONITH object */ external_getinfo, /* Return STONITH info string */ external_get_confignames, /* Return STONITH info string */ external_set_config, /* Get configuration from NVpairs */ external_status, /* Return STONITH device status */ external_reset_req, /* Request a reset */ external_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &externalOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * EXTERNAL STONITH device */ struct pluginDevice { StonithPlugin sp; const char * pluginid; GHashTable * cmd_opts; char * subplugin; char ** confignames; char * outputbuf; }; static const char * pluginid = "ExternalDevice-Stonith"; static const char * NOTpluginID = "External device has been destroyed"; /* Prototypes */ /* Run the command with op and return the exit status + the output * (NULL -> discard output) */ static int external_run_cmd(struct pluginDevice *sd, const char *op, char **output); /* Just free up the configuration and the memory, if any */ static void external_unconfig(struct pluginDevice *sd); static int external_status(StonithPlugin *s) { struct pluginDevice * sd; const char * op = "status"; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } rc = external_run_cmd(sd, op, NULL); if (rc != 0) { LOG(PIL_WARN, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); } else { if (Debug) { LOG(PIL_DEBUG, "%s: running '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } } return rc; } static int get_num_tokens(char *str) { int namecount = 0; while (*str != EOS) { str += strspn(str, WHITESPACE); if (*str == EOS) break; str += strcspn(str, WHITESPACE); namecount++; } return namecount; } static char ** external_hostlist(StonithPlugin *s) { struct pluginDevice* sd; const char * op = "gethosts"; int rc, i, namecount; char ** ret; char * output = NULL; char * tmp; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,NULL); sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(NULL); } rc = external_run_cmd(sd, op, &output); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); if (output) { LOG(PIL_CRIT, "plugin output: %s", output); FREE(output); } return NULL; } if (Debug) { LOG(PIL_DEBUG, "%s: running '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } if (!output) { LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist", __FUNCTION__, sd->subplugin, op); return NULL; } namecount = get_num_tokens(output); ret = MALLOC((namecount+1)*sizeof(char *)); if (!ret) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); FREE(output); return NULL; } memset(ret, 0, (namecount+1)*sizeof(char *)); /* White-space split the output here */ i = 0; tmp = strtok(output, WHITESPACE); while (tmp != NULL) { if (Debug) { LOG(PIL_DEBUG, "%s: %s host %s", __FUNCTION__, sd->subplugin, tmp); } ret[i] = STRDUP(tmp); if (!ret[i]) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); FREE(output); stonith_free_hostlist(ret); return NULL; } i++; tmp = strtok(NULL, WHITESPACE); } FREE(output); if (i == 0) { LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist", __FUNCTION__, sd->subplugin, op); stonith_free_hostlist(ret); ret = NULL; } return(ret); } static int external_reset_req(StonithPlugin * s, int request, const char * host) { struct pluginDevice * sd; const char * op; int rc; char * args1and2; int argslen; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); if (Debug) { LOG(PIL_DEBUG, "Host external-reset initiating on %s", host); } sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } switch (request) { case ST_GENERIC_RESET: op = "reset"; break; case ST_POWEROFF: op = "off"; break; case ST_POWERON: op = "on"; break; default: LOG(PIL_CRIT, "%s: Unknown stonith request %d", __FUNCTION__, request); return S_OOPS; break; } argslen = strlen(op) + strlen(host) + 2 /* 1 for blank, 1 for EOS */; args1and2 = (char *)MALLOC(argslen); if (args1and2 == NULL) { LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__); return S_OOPS; } rc = snprintf(args1and2, argslen, "%s %s", op, host); if (rc <= 0 || rc >= argslen) { FREE(args1and2); return S_OOPS; } rc = external_run_cmd(sd, args1and2, NULL); FREE(args1and2); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d", __FUNCTION__, sd->subplugin, op, host, rc); return S_RESETFAIL; } else { if (Debug) { LOG(PIL_DEBUG, "%s: running '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } return S_OK; } } static int external_parse_config_info(struct pluginDevice* sd, StonithNVpair * info) { char * key; char * value; StonithNVpair * nv; sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal); /* TODO: Maybe treat "" as delimeters too so * whitespace can be passed to the plugins... */ for (nv = info; nv->s_name; nv++) { if (!nv->s_name || !nv->s_value) { continue; } key = STRDUP(nv->s_name); if (!key) { goto err_mem; } value = STRDUP(nv->s_value); if (!value) { FREE(key); goto err_mem; } g_hash_table_insert(sd->cmd_opts, key, value); } return(S_OK); err_mem: LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__); external_unconfig(sd); return(S_OOPS); } static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data) { if (key) { FREE(key); } if (value) { FREE(value); } return TRUE; } static void external_unconfig(struct pluginDevice *sd) { if (sd->cmd_opts) { g_hash_table_foreach_remove(sd->cmd_opts, let_remove_eachitem, NULL); g_hash_table_destroy(sd->cmd_opts); sd->cmd_opts = NULL; } } /* * Parse the information in the given string * and stash it away... */ static int external_set_config(StonithPlugin* s, StonithNVpair *list) { struct pluginDevice * sd; char ** p; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); /* make sure that command has not already been set */ if (s->isconfigured) { return(S_OOPS); } sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } if (sd->confignames == NULL) { /* specified by name=value pairs, check required parms */ if (external_get_confignames(s) == NULL) { return(S_OOPS); } for (p = sd->confignames; *p; p++) { if (OurImports->GetValue(list, *p) == NULL) { LOG(PIL_DEBUG, "Cannot get parameter %s from " "StonithNVpair", *p); } } } return external_parse_config_info(sd, list); } /* Only interested in regular files that are also executable */ static int exec_select(const struct dirent *dire) { struct stat statf; char filename[FILENAME_MAX]; int rc; rc = snprintf(filename, FILENAME_MAX, "%s/%s", STONITH_EXT_PLUGINDIR, dire->d_name); if (rc <= 0 || rc >= FILENAME_MAX) { return 0; } if ((stat(filename, &statf) == 0) && (S_ISREG(statf.st_mode)) && (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) { if (statf.st_mode & (S_IWGRP|S_IWOTH)) { LOG(PIL_WARN, "Executable file %s ignored " "(writable by group/others)", filename); return 0; }else{ return 1; } } return 0; } /* * Return STONITH config vars */ static const char * const * external_get_confignames(StonithPlugin* p) { struct pluginDevice * sd; const char * op = "getconfignames"; int i, rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } sd = (struct pluginDevice *)p; if (sd->subplugin != NULL) { /* return list of subplugin's required parameters */ char *output = NULL, *pch; int namecount; rc = external_run_cmd(sd, op, &output); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); if (output) { LOG(PIL_CRIT, "plugin output: %s", output); FREE(output); } return NULL; } if (Debug) { LOG(PIL_DEBUG, "%s: '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); if (output) { LOG(PIL_DEBUG, "plugin output: %s", output); } } namecount = get_num_tokens(output); sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *)); if (sd->confignames == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); if (output) { FREE(output); } return NULL; } /* now copy over confignames */ pch = strtok(output, WHITESPACE); for (i = 0; i < namecount; i++) { if (Debug) { LOG(PIL_DEBUG, "%s: %s configname %s", __FUNCTION__, sd->subplugin, pch); } sd->confignames[i] = STRDUP(pch); pch = strtok(NULL, WHITESPACE); } FREE(output); sd->confignames[namecount] = NULL; }else{ /* return list of subplugins in external directory */ struct dirent ** files = NULL; int dircount; /* get the external plugin's confignames (list of subplugins) */ dircount = scandir(STONITH_EXT_PLUGINDIR, &files, SCANSEL_CAST exec_select, NULL); if (dircount < 0) { return NULL; } sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *)); if (!sd->confignames) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return NULL; } for (i = 0; i < dircount; i++) { sd->confignames[i] = STRDUP(files[i]->d_name); free(files[i]); files[i] = NULL; } free(files); sd->confignames[dircount] = NULL; } return (const char * const *)sd->confignames; } /* * Return STONITH info string */ static const char * external_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* sd; char * output = NULL; const char * op; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); sd = (struct pluginDevice *)s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(NULL); } switch (reqtype) { case ST_DEVICEID: op = "getinfo-devid"; break; case ST_DEVICENAME: op = "getinfo-devname"; break; case ST_DEVICEDESCR: op = "getinfo-devdescr"; break; case ST_DEVICEURL: op = "getinfo-devurl"; break; case ST_CONF_XML: op = "getinfo-xml"; break; default: return NULL; } rc = external_run_cmd(sd, op, &output); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); if (output) { LOG(PIL_CRIT, "plugin output: %s", output); FREE(output); } } else { if (Debug) { LOG(PIL_DEBUG, "%s: '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } if (sd->outputbuf != NULL) { FREE(sd->outputbuf); } sd->outputbuf = output; return(output); } return(NULL); } /* * EXTERNAL Stonith destructor... */ static void external_destroy(StonithPlugin *s) { struct pluginDevice * sd; char ** p; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice *)s; sd->pluginid = NOTpluginID; external_unconfig(sd); if (sd->confignames != NULL) { for (p = sd->confignames; *p; p++) { FREE(*p); } FREE(sd->confignames); sd->confignames = NULL; } if (sd->subplugin != NULL) { FREE(sd->subplugin); sd->subplugin = NULL; } if (sd->outputbuf != NULL) { FREE(sd->outputbuf); sd->outputbuf = NULL; } FREE(sd); } /* Create a new external Stonith device */ static StonithPlugin * external_new(const char *subplugin) { struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; if (subplugin != NULL) { sd->subplugin = STRDUP(subplugin); if (sd->subplugin == NULL) { FREE(sd); return(NULL); } } sd->sp.s_ops = &externalOps; return &(sd->sp); } static void ext_add_to_env(gpointer key, gpointer value, gpointer user_data) { if (setenv((char *)key, (char *)value, 1) != 0) { LOG(PIL_CRIT, "%s: setenv failed.", __FUNCTION__); } } static void ext_del_from_env(gpointer key, gpointer value, gpointer user_data) { unsetenv((char *)key); } #define LOGTAG_VAR "HA_LOGTAG" /* Run the command with op as command line argument(s) and return the exit * status + the output */ static int external_run_cmd(struct pluginDevice *sd, const char *op, char **output) { const int BUFF_LEN=4096; char buff[BUFF_LEN]; int read_len = 0; int status, rc; char * data = NULL; FILE * file; char cmd[FILENAME_MAX+64]; struct stat buf; int slen; char *path, *new_path, *logtag, *savevar = NULL; int new_path_len, logtag_len; gboolean nodata; rc = snprintf(cmd, FILENAME_MAX, "%s/%s", STONITH_EXT_PLUGINDIR, sd->subplugin); if (rc <= 0 || rc >= FILENAME_MAX) { LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__); return -1; } if (stat(cmd, &buf) != 0) { LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s", __FUNCTION__, cmd, strerror(errno)); return -1; } if (!S_ISREG(buf.st_mode) || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { LOG(PIL_CRIT, "%s: %s found NOT to be executable.", __FUNCTION__, cmd); return -1; } if (buf.st_mode & (S_IWGRP|S_IWOTH)) { LOG(PIL_CRIT, "%s: %s found to be writable by group/others, " "NOT executing for security purposes.", __FUNCTION__, cmd); return -1; } strcat(cmd, " "); strcat(cmd, op); /* We only have a global environment to use here. So we add our * options to it, and then later remove them again. */ if (sd->cmd_opts) { g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL); } /* external plugins need path to ha_log.sh */ path = getenv("PATH"); if (strncmp(GLUE_SHARED_DIR,path,strlen(GLUE_SHARED_DIR))) { new_path_len = strlen(path)+strlen(GLUE_SHARED_DIR)+2; new_path = (char *)g_malloc(new_path_len); snprintf(new_path, new_path_len, "%s:%s", GLUE_SHARED_DIR, path); setenv("PATH", new_path, 1); g_free(new_path); } /* set the logtag appropriately */ logtag_len = strlen(PIL_PLUGIN_S)+strlen(sd->subplugin)+2; logtag = (char *)g_malloc(logtag_len); snprintf(logtag, logtag_len, "%s/%s", PIL_PLUGIN_S, sd->subplugin); if (getenv(LOGTAG_VAR)) { savevar = g_strdup(getenv(LOGTAG_VAR)); } setenv(LOGTAG_VAR, logtag, 1); if (Debug) { LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd ); } file = popen(cmd, "r"); if (NULL==file) { LOG(PIL_CRIT, "%s: Calling '%s' failed", __FUNCTION__, cmd); rc = -1; goto out; } if (output) { slen=0; data = MALLOC(1); data[slen] = EOS; } while (!feof(file)) { nodata = TRUE; if (output) { read_len = fread(buff, 1, BUFF_LEN, file); if (read_len > 0) { data = REALLOC(data, slen+read_len+1); if (data == NULL) { break; } memcpy(data + slen, buff, read_len); slen += read_len; data[slen] = EOS; nodata = FALSE; } } else { if (fgets(buff, BUFF_LEN, file)) { LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, buff); nodata = FALSE; } } if (nodata) { sleep(1); } } if (output && !data) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); rc = -1; goto out; } status = pclose(file); if (WIFEXITED(status)) { rc = WEXITSTATUS(status); if (rc != 0 && Debug) { LOG(PIL_DEBUG, "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc); } } else { if (WIFSIGNALED(status)) { LOG(PIL_CRIT, "%s: '%s' got signal %d", __FUNCTION__, cmd, WTERMSIG(status)); } else if (WIFSTOPPED(status)) { LOG(PIL_INFO, "%s: '%s' stopped with signal %d", __FUNCTION__, cmd, WSTOPSIG(status)); } else { LOG(PIL_CRIT, "%s: '%s' exited abnormally (core dumped?)", __FUNCTION__, cmd); } rc = -1; } if (Debug && output && data) { LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data); } out: if (savevar) { setenv(LOGTAG_VAR, savevar, 1); g_free(savevar); } else { unsetenv(LOGTAG_VAR); } if (sd->cmd_opts) { g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL); } if (!rc) { if (output) { *output = data; } } else { if (data) { FREE(data); } if (output) { *output = NULL; } } return rc; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/Makefile.am0000644000000000000000000000235712120057602031065 0ustar00usergroup00000000000000# Makefile.am for OCF RAs # # Author: Sun Jing Dong # Copyright (C) 2004 IBM # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = drac5 dracmc-telnet ibmrsa-telnet ipmi rackpdu vmware vcenter xen0 \ xen0-ha-dom0-stonith-helper kdumpcheck nut extdir = $(stonith_ext_plugindir) helperdir = $(stonith_plugindir) ext_SCRIPTS = drac5 dracmc-telnet ibmrsa ibmrsa-telnet ipmi riloe ssh vmware vcenter rackpdu xen0 hmchttp \ xen0-ha kdumpcheck ippower9258 nut libvirt \ hetzner helper_SCRIPTS = xen0-ha-dom0-stonith-helper Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/drac5.in0000644000000000000000000000332212120057602030350 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for DRAC5 adapters. # # Author: Jun Wang # License: GNU General Public License (GPL) # trap 'if [ -n "$outf" ]; then ha_log.sh err "`cat $outf`"; rm -f "$outf"; fi' 0 outf=`mktemp` || { ha_log.sh err "mktemp failed" exit 1 } sshlogin() { if [ x = "x$ipaddr" -o x = "x$userid" ] then ha_log.sh err "ipaddr or userid missing; check configuration" return 1 fi @SSH@ -q -x -n $userid@$ipaddr racadm serveraction "$1" >$outf 2>&1 } drac_reset() { sshlogin hardreset } drac_on() { sshlogin poweron } drac_off() { sshlogin poweroff } drac_status() { sshlogin powerstatus } case $1 in gethosts) echo $hostname ;; on) drac_poweron ;; off) drac_poweroff ;; reset) drac_reset ;; status) drac_status ;; getconfignames) for i in hostname ipaddr userid; do echo $i done ;; getinfo-devid) echo "DRAC5 STONITH device" ;; getinfo-devname) echo "DRAC5 STONITH device" ;; getinfo-devdescr) echo "DRAC5 host reset/poweron/poweroff" ;; getinfo-devurl) echo "http://www.dell.com" ;; getinfo-xml) cat < Hostname The hostname of the host to be managed by this STONITH device IP Address The IP address of the STONITH device Login The username used for logging in to the STONITH device EOF ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/dracmc-telnet0000644000000000000000000003356312120057602031501 0ustar00usergroup00000000000000#!/usr/bin/env python # vim: set filetype=python ####################################################################### # # dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki) # Connects to Dell Drac/MC Blade Enclosure via a Cyclades # terminal server with telnet and switches power of named # blade servers appropriatelly. # # Required parameters: # nodename: The name of the server you want to touch on your network # cyclades_ip: The IP address of the cyclades terminal server # cyclades_port: The port for telnet to access on the cyclades (i.e. 7032) # servername: The DRAC/MC server name of the blade (i.e. Server-7) # username: The login user name for the DRAC/MC # password: The login password for the DRAC/MC # # Author: Alex Tsariounov # # Based on ibmrsa-telnet external stonith plugin by Andreas Mock # (andreas.mock@web.de), Copyright by Adreas Mock and released as part # of HAv2. # # History: # 2009-10-12 First release. # # Copyright (c) 2009 Novell, Inc. # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 or later of the GNU General Public # License as published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### import sys import os import time import telnetlib import random import subprocess LOGINRETRIES = 10 class TimeoutException(Exception): def __init__(self, value=None): Exception.__init__(self) self.value = value def __str__(self): return repr(self.value) class DracMC(telnetlib.Telnet): def __init__(self, *args, **kwargs): telnetlib.Telnet.__init__(self) self._timeout = 4 self._loggedin = 0 self._history = [] self._appl = os.path.basename(sys.argv[0]) self._server = args[0] def _get_timestamp(self): ct = time.time() msecs = (ct - long(ct)) * 1000 return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct)), msecs) def write(self, buffer): self._history.append(self._get_timestamp() + ': WRITE: ' + repr(buffer)) telnetlib.Telnet.write(self, buffer) def read_until(self, what, timeout=2): line = telnetlib.Telnet.read_until(self, what, timeout) self._history.append(self._get_timestamp() + ': READ : ' + repr(line)) if not line.endswith(what): raise TimeoutException("Timeout while waiting for '%s'." % (what, )) return line def login(self, user, passwd): time.sleep(0.3) try: line = self.read_until('Login: ', self._timeout) self.write(user) self.write('\r') line = self.read_until('Password: ', self._timeout) self.write(passwd) self.write('\r') except: self.write("\r") line = self.read_until('Login: ', self._timeout) self.write(user) self.write('\r') line = self.read_until('Password: ', self._timeout) self.write(passwd) self.write('\r') try: line = self.read_until('DRAC/MC:', self._timeout) except: self.write("\r") line = self.read_until('DRAC/MC:', self._timeout) def hardreset(self): self.write('serveraction -s %s hardreset\r' % self._server) line = self.read_until('OK', 10) line = self.read_until('DRAC/MC:', self._timeout) def powercycle(self): self.write('serveraction -s %s powercycle\r' % self._server) line = self.read_until('OK', 10) line = self.read_until('DRAC/MC:', self._timeout) def on(self): self.write('serveraction -s %s powerup\r' % self._server) line = self.read_until('OK', 10) line = self.read_until('DRAC/MC:', self._timeout) def off(self): self.write('serveraction -s %s powerdown\r' % self._server) line = self.read_until('OK', 10) line = self.read_until('DRAC/MC:', self._timeout) def exit(self): self.write('exit\r') def get_history(self): return "\n".join(self._history) class DracMCStonithPlugin: def __init__(self): # define the external stonith plugin api self._required_cmds = \ 'reset gethosts status getconfignames getinfo-devid ' \ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \ 'getinfo-xml' self._optional_cmds = 'on off' self._required_cmds_list = self._required_cmds.split() self._optional_cmds_list = self._optional_cmds.split() # who am i self._appl = os.path.basename(sys.argv[0]) # telnet connection object self._connection = None # the list of configuration names self._confignames = ['nodename', 'cyclades_ip', 'cyclades_port', 'servername', 'username', 'password'] # catch the parameters provided by environment self._parameters = {} for name in self._confignames: try: self._parameters[name] = os.environ.get(name, '').split()[0] except IndexError: self._parameters[name] = '' def _get_timestamp(self): ct = time.time() msecs = (ct - long(ct)) * 1000 return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct)), msecs) def _echo_debug(self, *args): subprocess.call("ha_log.sh debug '%s'" % ' '.join(args), shell=True) def echo(self, *args): what = ''.join([str(x) for x in args]) sys.stdout.write(what) sys.stdout.write('\n') sys.stdout.flush() self._echo_debug("STDOUT:", what) def echo_log(self, level, *args): subprocess.call("ha_log.sh %s '%s'" % (level,' '.join(args)), shell=True) def _get_connection(self): if not self._connection: c = DracMC(self._parameters['servername']) self._echo_debug("Connecting to '%s:%s'" % (self._parameters['cyclades_ip'], self._parameters['cyclades_port'])) tries = 0 while tries < LOGINRETRIES: try: c.open(self._parameters['cyclades_ip'], self._parameters['cyclades_port']) c.login(self._parameters['username'], self._parameters['password']) except Exception, args: if "Connection reset by peer" in str(args): self._echo_debug("Someone is already logged in... retry=%s" % tries) c.close() time.sleep(random.uniform(1.0, 5.0)) else: raise else: break tries += 1 if tries == LOGINRETRIES: c.close() raise Exception("Could not log in to %s:%s" % (self._parameters['cyclades_ip'], self._parameters['cyclades_port'])) self._connection = c def _end_connection(self): if self._connection: self._connection.exit() self._connection.close() def reset(self): self._get_connection() # self._connection.hardreset() self._connection.powercycle() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Reset of node '%s' done" % (self._parameters['nodename'],)) return(0) def on(self): self._get_connection() self._connection.on() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Switched node '%s' ON" % (self._parameters['nodename'],)) return(0) def off(self): self._get_connection() self._connection.off() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Switched node '%s' OFF" % (self._parameters['nodename'],)) return(0) def gethosts(self): self.echo(self._parameters['nodename']) return(0) def status(self): self._get_connection() self._end_connection() self._echo_debug(self._connection.get_history()) return(0) def getconfignames(self): for name in ['nodename', 'cyclades_ip', 'cyclades_port', 'servername', 'username', 'password']: self.echo(name) return(0) def getinfo_devid(self): self.echo("External Stonith Plugin for Dell DRAC/MC via Cyclades") return(0) def getinfo_devname(self): self.echo("External Stonith Plugin for Dell Drac/MC connecting " "via Telnet to a Cyclades port") return(0) def getinfo_devdescr(self): self.echo("External stonith plugin for HAv2 which connects to " "a Dell DRAC/MC connected via a Cyclades port with telnet. " "Commands to turn on/off power and to reset server are sent " "appropriately. " "(c) 2009 by Novell, Inc. (alext@novell.com)") return(0) def getinfo_devurl(self): self.echo("http://support.dell.com/support/edocs/software/smdrac3/dracmc/1.3/en/index.htm") def getinfo_xml(self): info = """ nodename to shoot Name of the node to be stonithed. hostname or ip address of cyclades Hostname or IP address of Cyclades connected to DRAC/MC. telnet port to use on cyclades Port used with the Cyclades telnet interface which is connected to the DRAC/MC. DRAC/MC name of blade to be stonithed Name of server blade to be stonithed on the DRAC/MC (example: Server-7) username to login on the DRAC/MC Username to login to the DRAC/MC once connected via the Cyclades port. password to login on the DRAC/MC Password to login to the DRAC/MC once connected via the Cyclades port. """ self.echo(info) return(0) def not_implemented(self, cmd): self.echo_log("err", "Command '%s' not implemented." % (cmd,)) return(1) def usage(self): usage = "Call me with one of the allowed commands: %s, %s" % ( ', '.join(self._required_cmds_list), ', '.join(self._optional_cmds_list)) return usage def process(self, argv): self._echo_debug("========== Start =============") if len(argv) < 1: self.echo_log("err", 'At least one commandline argument required.') return(1) cmd = argv[0] self._echo_debug("cmd:", cmd) if cmd not in self._required_cmds_list and \ cmd not in self._optional_cmds_list: self.echo_log("err", "Command '%s' not supported." % (cmd,)) return(1) try: cmd = cmd.lower().replace('-', '_') func = getattr(self, cmd, self.not_implemented) rc = func() return(rc) except Exception, args: self.echo_log("err", 'Exception raised:', str(args)) if self._connection: self.echo_log("err", self._connection.get_history()) self._connection.close() return(1) if __name__ == '__main__': stonith = DracMCStonithPlugin() rc = stonith.process(sys.argv[1:]) sys.exit(rc) Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/hetzner0000755000000000000000000001000212120057602030420 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for Hetzner. # # Copyright (c) 2011 MMUL S.a.S. - Raoul Scarazzini # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # # Read parameters from config file, format is based upon the hetzner OCF resource agent # developed by Kumina: http://blog.kumina.nl/2011/02/hetzner-failover-ip-ocf-script/ conf_file="/etc/hetzner.cfg" case $1 in get*) ;; # don't print errors if conf_file not present *) user=`sed -n 's/^user.*=\ *//p' $conf_file` pass=`sed -n 's/^pass.*=\ *//p' $conf_file` ;; esac hetzner_server="https://robot-ws.your-server.de" check_http_response() { # If the response is 200 then return 0 if [ $1 = 200 ] then return 0 else # If the response is not 200 then display a description of the problem and return 1 case $1 in 400) ha_log.sh err "INVALID_INPUT - Invalid input parameters" ;; 404) ha_log.sh err "SERVER_NOT_FOUND - Server with ip $remote_ip not found" ;; 409) ha_log.sh err "RESET_MANUAL_ACTIVE - There is already a running manual reset" ;; 500) ha_log.sh err "RESET_FAILED - Resetting failed due to an internal error" ;; esac return 1 fi } case $1 in gethosts) echo $hostname exit 0 ;; on) # Can't really be implemented because Hetzner's webservice cannot power on a system ha_log.sh err "Power on is not available since Hetzner's webservice can't do this operation." exit 1 ;; off) # Can't really be implemented because Hetzner's webservice cannot power on a system ha_log.sh err "Power off is not available since Hetzner's webservice can't do this operation." exit 1 ;; reset) # Launching the reset action via webservice check_http_response $(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/reset/$remote_ip -d type=hw) exit $? ;; status) # Check if we can contact the webservice check_http_response "$(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/server/$remote_ip)" exit $? ;; getconfignames) echo "hostname" echo "remote_ip" exit 0 ;; getinfo-devid) echo "Hetzner STONITH device" exit 0 ;; getinfo-devname) echo "Hetzner STONITH external device" exit 0 ;; getinfo-devdescr) echo "Hetzner host reset" echo "Manages the remote webservice for reset a remote server." exit 0 ;; getinfo-devurl) echo "http://wiki.hetzner.de/index.php/Robot_Webservice_en" exit 0 ;; getinfo-xml) cat << HETZNERXML Hostname The name of the host to be managed by this STONITH device. Remote IP The address of the remote IP that manages this server. HETZNERXML exit 0 ;; *) ha_log.sh err "Don't know what to do for '$remote_ip'" exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/hmchttp0000644000000000000000000001233312120057602030416 0ustar00usergroup00000000000000#!/bin/sh # External STONITH module for HMC web console # # Copyright (c) 2007 Xinwei Hu # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # #set -x hostlist=`echo $hostlist | tr ',' ' '` trap '[ ! -e "$COOKIEFILE" ] || rm -f "$COOKIEFILE"' 0 COOKIEFILE=`mktemp` || exit 1 : ${CURLBIN="/usr/bin/curl"} : ${user=admin} : ${password=admin} check_parameter() { if [ ! -x $CURLBIN ] then ha_log.sh err "Curl can't be found in normal place. Set CURLBIN to override the default value" exit 1 fi if [ -z $hmc_ipaddr ] then ha_log.sh err "The address of HMC web console is not specified" exit 1 fi } HMCUSERNAME=$user HMCPASSWORD=$password HMC_LOGIN_COMMAND="$CURLBIN -3 -k -c $COOKIEFILE -d user=$HMCUSERNAME -d password=$HMCPASSWORD -d lang=0 -d submit=Log+in " HMC_LOGOUT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d submit=Log+out " HMC_TOC_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=2 " HMC_POWERON_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 -d sp=255 -d is=0 -d om=4 -d id=1 -d ip=2 -d plt=1 -d pm=0 -d on=Save+settings+and+power+on " HMC_POWERSTATE_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 " HMC_POWEROFF_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=30 -d submit=Continue " HMC_REBOOT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=74 -d submit=Continue " hmc_login() { iamin=0 while [ $iamin -eq 0 ]; do $HMC_LOGIN_COMMAND https://$hmc_ipaddr/cgi-bin/cgi >/dev/null 2>&1 $HMC_TOC_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Too many users" iamin=$? sleep 2 done } hmc_logout() { $HMC_LOGOUT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null } hmc_reboot() { check_parameter $HMC_REBOOT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null } hmc_poweron() { r=1 while [ 0 -ne $r ]; do $HMC_POWERON_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Operation completed successfully" r=$? done } hmc_poweroff() { check_parameter $HMC_POWEROFF_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null } hmc_powerstate() { check_parameter r=`$HMC_POWERSTATE_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null| grep "Current system power state:" | sed 's/
//g' | awk '{print $5}'` echo $r } hmc_poweroffon() { check_parameter hmc_poweroff while [ 1 ]; do r=`hmc_powerstate` ha_log.sh debug "power state: $r" if [ $r = "Off" ]; then break fi sleep 5 done sleep 3 hmc_poweron } case $1 in gethosts) for h in $hostlist; do echo $h done exit 0 ;; status) if ping -w1 -c1 "$hmc_ipaddr" 2>&1 then exit 0 fi exit 1 ;; getconfignames) for f in hostlist hmc_ipaddr user password; do echo $f done exit 0 ;; getinfo-devid) echo "HMC web console STONITH device" exit 0 ;; getinfo-devname) echo "HMC web console STONITH external device" exit 0 ;; getinfo-devdescr) echo "HMC web console based host power control" echo "Use for i5, p5, pSeries and OpenPower systems that are managed via " echo "web console through a direct connection to system's HMC port." exit 0 ;; getinfo-devurl) echo "http://www.ibm.com" exit 0 ;; getinfo-xml) cat << HMCXML Hostlist The list of hosts that the STONITH device controls HMC IPAddr The IP address of the HMC web console User User name to log into HMC web console Password The password of user name to log into HMC web console HMCXML exit 0 ;; esac case $1 in on|off|reset|powerstate|poweroffon) hmc_login case $1 in on) hmc_poweron $hmc_ipaddr ;; off) hmc_poweroff $hmc_ipaddr ;; reset) # hmc_reboot $hmc_ipaddr hmc_poweroffon $hmc_ipaddr ;; powerstate) hmc_powerstate ;; poweroffon) hmc_poweroffon ;; esac hmc_logout exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/ibmrsa0000644000000000000000000000553712120057602030234 0ustar00usergroup00000000000000#!/bin/sh # # Copyright (c) 2006 Dejan Muhamedagic , IBM Austria # # External STONITH module for IBM RSA adapters. # External STONITH module for IBM BMC. # This STONITH module depends on IBMmpcli. # trap 'rm -f "$outf"' 0 outf=`mktemp` || { ha_log.sh err 'mktemp failed' exit 1 } chkmpcli() { test -x /opt/IBMmpcli/bin/MPCLI.sh } mpcli() { chkmpcli || { ha_log.sh err "IBM mpcli not installed" return 1 } if [ x = "x$ipaddr" -o x = "x$userid" -o x = "x$passwd" ] then ha_log.sh err "ipaddr, userid, or passwd missing; check configuration" return 1 fi type=${type:-"ibm"} goodstg="SUCCESS" failstg="FAILURE" ( echo "logonip -h $ipaddr -u $userid -p $passwd -t $type" echo "outputfile $outf" cat ) | /opt/IBMmpcli/bin/MPCLI.sh | grep -w $goodstg >/dev/null 2>&1 rc=$? grep -w $failstg $outf >/dev/null if [ $rc -eq 0 -a $? -eq 1 ]; then return 0 else ha_log.sh err "MPCLI.sh failed: `cat $outf`" return 1 fi } ibmrsa_reboot() { echo restart -now | mpcli } ibmrsa_poweron() { echo poweron | mpcli } ibmrsa_poweroff() { echo poweroff | mpcli } ibmrsa_status() { echo | mpcli } hostname=`echo ${hostname} | tr ',' ' '` case $1 in gethosts) echo $hostname ;; on) ibmrsa_poweron ;; off) ibmrsa_poweroff ;; reset) ibmrsa_reboot ;; status) ibmrsa_status ;; getconfignames) for i in hostname ipaddr userid passwd type; do echo $i done ;; getinfo-devid) echo "IBM MP STONITH device" ;; getinfo-devname) echo "IBM MP STONITH device" ;; getinfo-devdescr) echo "IBM MP host reboot/poweron/poweroff" ;; getinfo-devurl) echo "http://www.ibm.com" ;; getinfo-xml) cat < Hostname The hostname of the host to be managed by this STONITH device IP Address The IP address of the STONITH device Login The username used to login into the STONITH device Password The password used to login into the STONITH device Management processor type The type of the management processor. Possible values are "ibm" (default, typically used for RSA) and "ipmi" (for IPMI compliant processors such as BMC). EOF ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/ibmrsa-telnet0000644000000000000000000002652512120057602031525 0ustar00usergroup00000000000000#!/usr/bin/python # vim: set filetype=python ####################################################################### # # ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki) # Connects to IBM RSA Board via telnet and switches power # of server appropriately. # # Author: Andreas Mock (andreas.mock@web.de) # # History: # 2007-10-19 Fixed bad commandline handling in case of stonithing # 2007-10-11 First release. # # Comment: Please send bug fixes and enhancements. # I hope the functionality of communicating via telnet is encapsulated # enough so that someone can use it for similar purposes. # # Description: IBM offers Remote Supervisor Adapters II for several # servers. These RSA boards can be accessed in different ways. # One of that is via telnet. Once logged in you can use 'help' to # show all available commands. With 'power' you can reset, power on and # off the controlled server. This command is used in combination # with python's standard library 'telnetlib' to do it automatically. # # cib-snippet: Please see README.ibmrsa-telnet for examples. # # Copyright (c) 2007 Andreas Mock (andreas.mock@web.de) # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 or later of the GNU General Public # License as published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### import sys import os import time import telnetlib import subprocess class TimeoutException(Exception): def __init__(self, value=None): Exception.__init__(self) self.value = value def __str__(self): return repr(self.value) class RSABoard(telnetlib.Telnet): def __init__(self, *args, **kwargs): telnetlib.Telnet.__init__(self, *args, **kwargs) self._timeout = 10 self._loggedin = 0 self._history = [] self._appl = os.path.basename(sys.argv[0]) def _get_timestamp(self): ct = time.time() msecs = (ct - long(ct)) * 1000 return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct)), msecs) def write(self, buffer, nolog = False): self._history.append(self._get_timestamp() + ': WRITE: ' + (nolog and '******' or repr(buffer))) telnetlib.Telnet.write(self, buffer) def expect(self, what, timeout=20): line = telnetlib.Telnet.expect(self, what, timeout) self._history.append(self._get_timestamp() + ': READ : ' + repr(line)) if not line: raise TimeoutException("Timeout while waiting for '%s'." % (what, )) return line def login(self, user, passwd): time.sleep(1) line = self.expect(['\nlogin : ', '\nusername: '], self._timeout) self.write(user) self.write('\r') line = self.expect(['\nPassword: ', '\npassword: '], self._timeout) self.write(passwd, nolog = True) self.write('\r') line = self.expect(['\nsystem>', '> '], self._timeout) def reset(self): self.write('power cycle\r') line = self.expect(['\nok'], self._timeout) line = self.expect(['\nsystem>', '> '], self._timeout) def on(self): self.write('power on\r') line = self.expect(['\nok'], self._timeout) line = self.expect(['\nsystem>', '> '], self._timeout) def off(self): self.write('power off\r') line = self.expect(['\nok'], self._timeout) line = self.expect(['\nsystem>', '> '], self._timeout) def exit(self): self.write('exit\r') def get_history(self): return "\n".join(self._history) class RSAStonithPlugin: def __init__(self): # define the external stonith plugin api self._required_cmds = \ 'reset gethosts status getconfignames getinfo-devid ' \ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \ 'getinfo-xml' self._optional_cmds = 'on off' self._required_cmds_list = self._required_cmds.split() self._optional_cmds_list = self._optional_cmds.split() # who am i self._appl = os.path.basename(sys.argv[0]) # telnet connection object self._connection = None # the list of configuration names self._confignames = ['nodename', 'ip_address', 'username', 'password'] # catch the parameters provided by environment self._parameters = {} for name in self._confignames: try: self._parameters[name] = os.environ.get(name, '').split()[0] except IndexError: self._parameters[name] = '' def _get_timestamp(self): ct = time.time() msecs = (ct - long(ct)) * 1000 return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct)), msecs) def _echo_debug(self, *args): self.echo_log('debug', *args) def echo(self, *args): what = ''.join([str(x) for x in args]) sys.stdout.write(what) sys.stdout.write('\n') sys.stdout.flush() self._echo_debug("STDOUT:", what) def echo_log(self, level, *args): subprocess.call(('ha_log.sh', level) + args) def _get_connection(self): if not self._connection: c = RSABoard() self._echo_debug("Connect to '%s'" % (self._parameters['ip_address'],)) c.open(self._parameters['ip_address']) self._echo_debug("Connection established") c.login(self._parameters['username'], self._parameters['password']) self._connection = c def _end_connection(self): if self._connection: self._connection.exit() self._connection.close() def reset(self): self._get_connection() self._connection.reset() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Reset of node '%s' done" % (self._parameters['nodename'],)) return(0) def on(self): self._get_connection() self._connection.on() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Switched node '%s' ON" % (self._parameters['nodename'],)) return(0) def off(self): self._get_connection() self._connection.off() self._end_connection() self._echo_debug(self._connection.get_history()) self.echo_log("info", "Switched node '%s' OFF" % (self._parameters['nodename'],)) return(0) def gethosts(self): self.echo(self._parameters['nodename']) return(0) def status(self): self._get_connection() self._end_connection() self._echo_debug(self._connection.get_history()) return(0) def getconfignames(self): for name in ['nodename', 'ip_address', 'username', 'password']: self.echo(name) return(0) def getinfo_devid(self): self.echo("External Stonith Plugin for IBM RSA Boards") return(0) def getinfo_devname(self): self.echo("External Stonith Plugin for IBM RSA Boards connecting " "via Telnet") return(0) def getinfo_devdescr(self): self.echo("External stonith plugin for HAv2 which connects to " "a RSA board on IBM servers via telnet. Commands to " "turn on/off power and to reset server are sent " "appropriately. " "(c) 2007 by Andreas Mock (andreas.mock@web.de)") return(0) def getinfo_devurl(self): self.echo("http://www.ibm.com/Search/?q=remote+supervisor+adapter") def getinfo_xml(self): info = """ nodename to shoot Name of the node which has to be stonithed in case. hostname or ip address of RSA Hostname or ip address of RSA board used to reset node. username to login on RSA board Username to login on RSA board. password to login on RSA board Password to login on RSA board. """ self.echo(info) return(0) def not_implemented(self, cmd): self.echo_log("err", "Command '%s' not implemented." % (cmd,)) return(1) def usage(self): usage = "Call me with one of the allowed commands: %s, %s" % ( ', '.join(self._required_cmds_list), ', '.join(self._optional_cmds_list)) return usage def process(self, argv): self._echo_debug("========== Start =============") if len(argv) < 1: self.echo_log("err", 'At least one commandline argument required.') return(1) cmd = argv[0] self._echo_debug("cmd:", cmd) if cmd not in self._required_cmds_list and \ cmd not in self._optional_cmds_list: self.echo_log("err", "Command '%s' not supported." % (cmd,)) return(1) try: cmd = cmd.lower().replace('-', '_') func = getattr(self, cmd, self.not_implemented) rc = func() return(rc) except Exception, args: self.echo_log("err", 'Exception raised:', str(args)) if self._connection: self.echo_log("err", self._connection.get_history()) self._connection.close() return(1) if __name__ == '__main__': stonith = RSAStonithPlugin() rc = stonith.process(sys.argv[1:]) sys.exit(rc) Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/ipmi0000644000000000000000000001501612120057602027706 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module using IPMI. # This modules uses uses the ipmitool program available from # http://ipmitool.sf.net/ for actual communication with the # managed device. # # Copyright (c) 2007 Martin Bene # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Initialization -- fix locale settings so we can parse output from # binaries if we need it LANG=C LC_ALL=C RESET="power reset" POWEROFF="power off" POWERON="power on" STATUS="power status" IPMITOOL=${ipmitool:-"`which ipmitool 2>/dev/null`"} have_ipmi() { test -x "${IPMITOOL}" } # Wrapper function for ipmitool that sets the correct host IP address, # username, and password, and invokes ipmitool with any arguments # passed in run_ipmitool() { local ipmitool_opts privlvl="" have_ipmi || { ha_log.sh err "ipmitool not installed" return 1 } if [ -z "${ipaddr}" -o -z "${userid}" -o -z "${passwd}" ]; then ha_log.sh err "ipaddr, userid or passwd missing; check configuration" return 1 fi if [ -z "${interface}" ]; then # default to "lan" interface interface="lan" fi if [ -n "${priv}" ]; then # default to "lan" interface privlvl="-L $priv" fi ipmitool_opts="-I ${interface} -H ${ipaddr} $privlvl" case "${passwd_method}" in param|'') passwd_method=param M="-P" ;; env) M="-E" ;; file) M="-f" ;; *) ha_log.sh err "invalid passwd_method: \"${passwd_method}\"" return 1 esac action="$*" if [ $passwd_method = env ] then IPMI_PASSWORD="${passwd}" ${IPMITOOL} $ipmitool_opts -U "${userid}" -E ${action} else ${IPMITOOL} $ipmitool_opts -U "${userid}" $M "${passwd}" ${action} fi 2>&1 } # Yet another convenience wrapper that invokes run_ipmitool, captures # its output, logs the output, returns either 0 (on success) or 1 (on # any error) do_ipmi() { if outp=`run_ipmitool $*`; then ha_log.sh debug "ipmitool output: `echo $outp`" return 0 else ha_log.sh err "error executing ipmitool: `echo $outp`" return 1 fi } # Check if the managed node is powered on. To do so, issue the "power # status" command. Should return either "Chassis Power is on" or # "Chassis Power is off". ipmi_is_power_on() { local outp outp=`run_ipmitool ${STATUS}` case "${outp}" in *on) return 0 ;; *off) return 1 ;; esac } case ${1} in gethosts) echo $hostname exit 0 ;; on) do_ipmi "${POWERON}" exit ;; off) do_ipmi "${POWEROFF}" exit ;; reset) if ipmi_is_power_on; then do_ipmi "${RESET}" else do_ipmi "${POWERON}" fi exit ;; status) # "status" reflects the status of the stonith _device_, not # the managed node. Hence, only check if we can contact the # IPMI device with "power status" command, don't pay attention # to whether the node is in fact powered on or off. do_ipmi "${STATUS}" exit $? ;; getconfignames) for i in hostname ipaddr userid passwd interface; do echo $i done exit 0 ;; getinfo-devid) echo "IPMI STONITH device" exit 0 ;; getinfo-devname) echo "IPMI STONITH external device" exit 0 ;; getinfo-devdescr) echo "ipmitool based power management. Apparently, the power off" echo "method of ipmitool is intercepted by ACPI which then makes" echo "a regular shutdown. If case of a split brain on a two-node" echo "it may happen that no node survives. For two-node clusters" echo "use only the reset method." exit 0 ;; getinfo-devurl) echo "http://ipmitool.sf.net/" exit 0 ;; getinfo-xml) cat << IPMIXML Hostname The name of the host to be managed by this STONITH device. IP Address The IP address of the STONITH device. Login The username used for logging in to the STONITH device. Password The password used for logging in to the STONITH device. Method for passing passwd parameter Method for passing the passwd parameter to ipmitool param: pass as parameter (-P) env: pass via environment (-E) file: value of "passwd" is actually a file name, pass with (-f) IPMI interface IPMI interface to use, such as "lan" or "lanplus". The privilege level of the user. The privilege level of the user, for instance OPERATOR. If unspecified the privilege level is ADMINISTRATOR. See ipmitool(1) -L option for more information. IPMI command(ipmitool) Specify the full path to IPMI command. IPMIXML exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/ippower9258.in0000755000000000000000000001740112120057602031375 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module using IP Power 9258 or compatible devices. # # Copyright (c) 2010 Helmut Weymann (Helmut (at) h-weymann (dot) de) # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # # # Basic commands & parameters independent from individual device DEVICE="IP Power 9258" IPPowerOn="1" IPPowerOff="0" IPGetPower="Set.cmd?CMD=GetPower" IPSetPower="Set.cmd?CMD=SetPower" IPPort_name="P" IPPort0=60 HTTP_COMMAND="wget -q -O - --" LOG_ERROR="ha_log.sh err" LOG_WARNING="ha_log.sh warn" LOG_INFO="ha_log.sh info" LOG_DEBUG="ha_log.sh debug" MY_COOKIES="cookies.txt" MY_TEMPFILE="temp.htm" PORT_STATUS="iocontrol.htm" UNDEFINED_HOSTNAME="*not-defined*" # # check MY_ROOT_PATH for IP Power 9258 and create it if necessary MY_ROOT_PATH="@GLUE_STATE_DIR@/heartbeat/rsctmp/ippower9258" # # script functions # get_challenge() { # # device sends a challenge for md5 encryption of username, password and challenge send_web_command - "http://$deviceip/" | grep Challenge | grep input | cut -d '"' -f 6 } get_cookie_from_device(){ # the form on the login page has these fields: # Username, Password, Challenge, Response, ScreenWidth # challenge=`get_challenge` response=`echo -n "$username$password$challenge" | md5sum | cut -b -32` postdata="Username=$username&Password=&Challenge=&Response=$response&ScreenWidth=1024" send_web_command " $MY_PATH/$MY_TEMPFILE --post-data=$postdata" "http://$deviceip/tgi/login.tgi" if grep -qs "Invalid User name or Password" $MY_PATH/$MY_TEMPFILE then $LOG_ERROR "Login to device $deviceip failed." $LOG_ERROR "Received Challenge = <<<$challenge>>>." $LOG_ERROR "Sent postdata = <<<$postdata>>>." exit 1 fi } get_data_from_device() { # If successful all device info is available in MY_PATH rm -f "$MY_PATH/$PORT_STATUS" send_web_command "$MY_PATH/$PORT_STATUS" "http://$deviceip/$PORT_STATUS" if grep -qs "Cookie Time Out" $MY_PATH/$PORT_STATUS then $LOG_ERROR "received no port data from $deviceip (Cookie Time Out)" exit 1 fi } send_http_request() { # ececution of http commands supported by the device $HTTP_COMMAND "http://$username:$password@$deviceip/$1" } send_web_command(){ # ececution of web commands through the web-interface WEB_COMMAND="wget -q --keep-session-cookies" WEB_COMMAND="$WEB_COMMAND --load-cookies $MY_PATH/$MY_COOKIES" WEB_COMMAND="$WEB_COMMAND --save-cookies $MY_PATH/$MY_COOKIES" $WEB_COMMAND -O $1 -- $2 } name2port() { local name=$1 local i=$IPPort0 for h in $device_hostlist ; do if [ $h = $name ]; then echo $IPPort_name$i return fi i=`expr $i + 1` done echo "invalid" } set_port() { # # port status is always set. Even if requested status is current status. # host status is not considered. local host=$1 local requested_status=$2 # 0 or 1 local port=`name2port $host` if [ "$port" = "invalid" ] then $LOG_ERROR "Host $host is not in hostlist ($hostlist) for $deviceip." exit 1 fi ret=`send_http_request "$IPSetPower+$port=$requested_status" | cut -b 11` if [ "$ret" != "$requested_status" ] then $LOG_ERROR "$DEVICE at $deviceip responds with wrong status $ret for host $host at port $port." exit 1 fi return 0 } build_device_hostlist() { # # hostnames are available from http://$deviceip/iocontrol.htm" # check for number of ports # device_hostlist=$( w3m -dump $MY_PATH/$PORT_STATUS | grep 'Power[1-8]' | sed 's/[^[]*\[//;s/\].*//;s/ *//' | while read h; do [ -z "$h" ] && echo $UNDEFINED_HOSTNAME || echo $h done ) if [ x = x"$device_hostlist" ]; then $LOG_ERROR "cannot get hostlist for $deviceip" exit 1 fi $LOG_DEBUG "Got new hostlist ($device_hostlist) from $deviceip" } filter_device_hostlist() { # check the given hostlist against the device hostlist local host for host in $device_hostlist; do [ "$host" != "$UNDEFINED_HOSTNAME" ] && echo $host done } check_hostlist() { # check the given hostlist against the device hostlist local cnt=`echo "$hostlist" | wc -w` local cnt2=0 local host for host in $hostlist; do if [ `name2port $host` != "invalid" ]; then cnt2=$((cnt2+1)) else $LOG_ERROR "host $host not defined at $deviceip" fi done [ $cnt -ne $cnt2 ] && exit 1 } get_http_status() { pattern="P60=[01],P61=[01],P62=[01],P63=[01],P64=[01],P65=[01],P66=[01],P67=[01]" ret=`send_http_request "$IPGetPower" | grep $pattern` if [ "X$ret" = "X" ] then $LOG_ERROR "$DEVICE at $deviceip returns invalid or no string." exit 1 fi } hostlist=`echo $hostlist | tr ',' ' '` case $1 in gethosts|on|off|reset|status) # need environment from stonithd # and device information from individual device # # default device username is admin # IP Power 9258 does not allow user management. # if [ "X$username" = "X" ] then username="admin" fi mkdir -p $MY_ROOT_PATH tmp_path="$deviceip" # ensure a simple unique pathname MY_PATH="$MY_ROOT_PATH/$tmp_path" mkdir -p $MY_PATH get_cookie_from_device get_data_from_device build_device_hostlist if [ "X$hostlist" = "X" ]; then hostlist="`filter_device_hostlist`" else check_hostlist fi ;; *) # the client is asking for meta-data ;; esac target=`echo $2 | sed 's/[.].*//'` # the necessary actions for stonithd case $1 in gethosts) echo $hostlist ;; on) set_port $target $IPPowerOn ;; off) set_port $target $IPPowerOff ;; reset) set_port $target $IPPowerOff sleep 5 set_port $target $IPPowerOn ;; status) # werify http command interface get_http_status ;; getconfignames) # return all the config names for ipparam in deviceip username password hostlist do echo $ipparam done ;; getinfo-devid) echo "IP Power 9258" ;; getinfo-devname) echo "IP Power 9258 power switch" ;; getinfo-devdescr) echo "Power switch IP Power 9258 with 4 or 8 power outlets." echo "WARNING: It is different from IP Power 9258 HP" ;; getinfo-devurl) echo "http://www.aviosys.com/manual.htm" ;; getinfo-xml) cat << IPPOWERXML IP address or hostname of the device. The IP Address or the hostname of the device. Password The password to log in with. Hostlist The list of hosts that the device controls. If you leave this list empty, we will retrieve the hostnames from the device. Account Name The user to log in with. IPPOWERXML ;; *) $LOG_ERROR "Unexpected command $1 for $DEVICE at $deviceip." exit 1; ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/kdumpcheck.in0000644000000000000000000001676612120057602031510 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module to check kdump. # # Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n" #Set default user name. USERNAME="kdumpchecker" #Initialize identity file-path options for ssh command IDENTITY_OPTS="" #Rewrite the hostlist to accept "," as a delimeter for hostnames too. hostlist=`echo ${hostlist} | tr ',' ' '` ## # Check the parameter hostlist is set or not. # If not, exit with 6 (ERR_CONFIGURED). ## check_hostlist() { if [ -z "${hostlist}" ]; then ha_log.sh err "hostlist is empty" exit 6 #ERR_CONFIGURED fi } ## # Set kdump check user name to USERNAME. # always return 0. ## get_username() { kdump_conf="/etc/kdump.conf" if [ ! -f "${kdump_conf}" ]; then ha_log.sh debug "${kdump_conf} doesn't exist" return 0 fi tmp="" while read config_opt config_val; do if [ "${config_opt}" = "kdump_check_user" ]; then tmp="${config_val}" fi done < "${kdump_conf}" if [ -n "${tmp}" ]; then USERNAME="${tmp}" fi ha_log.sh debug "kdump check user name is ${USERNAME}." } ## # Check the specified or default identity file exists or not. # If not, exit with 6 (ERR_CONFIGURED). ## check_identity_file() { IDENTITY_OPTS="" if [ -n "${identity_file}" ]; then if [ ! -f "${identity_file}" ]; then ha_log.sh err "${identity_file} doesn't exist." exit 6 #ERR_CONFIGURED fi IDENTITY_OPTS="-i ${identity_file}" else flg_file_exists=0 homedir=`eval echo "~${USERNAME}"` for filename in "${homedir}/.ssh/id_rsa" \ "${homedir}/.ssh/id_dsa" \ "${homedir}/.ssh/identity" do if [ -f "${filename}" ]; then flg_file_exists=1 IDENTITY_OPTS="${IDENTITY_OPTS} -i ${filename}" fi done if [ ${flg_file_exists} -eq 0 ]; then ha_log.sh err "${USERNAME}'s identity file for ssh command" \ " doesn't exist." exit 6 #ERR_CONFIGURED fi fi } ## # Check the user to check doing kdump exists or not. # If not, exit with 6 (ERR_CONFIGURED). ## check_user_existence() { # Get kdump check user name and check whether he exists or not. grep -q "^${USERNAME}:" /etc/passwd > /dev/null 2>&1 ret=$? if [ ${ret} != 0 ]; then ha_log.sh err "user ${USERNAME} doesn't exist." \ "please confirm \"kdump_check_user\" setting in /etc/kdump.conf." \ "(default user name is \"kdumpchecker\")" exit 6 #ERR_CONFIGURED fi } ## # Check the target node is kdumping or not. # arg1 : target node name. # ret : 0 -> the target is kdumping. # : 1 -> the target is _not_ kdumping. # : else -> failed to check. ## check_kdump() { target_node="$1" # Get kdump check user name. get_username check_user_existence exec_cmd="${SSH_COMMAND} -l ${USERNAME}" # Specify kdump check user's identity file for ssh command. check_identity_file exec_cmd="${exec_cmd} ${IDENTITY_OPTS}" # Now, check the target! # In advance, Write the following setting at the head of # kdump_check_user's public key in authorized_keys file on target node. # command="test -s /proc/vmcore", \ # no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ha_log.sh debug "execute the command [${exec_cmd} ${target_node}]." ${exec_cmd} ${target_node} > /dev/null 2>&1 ret=$? ha_log.sh debug "the command's result is ${ret}." #ret -> 0 : vmcore file's size is not zero. the node is kdumping. #ret -> 1 : the node is _not_ kdumping (vmcore didn't exist or # its size is zero). It still needs to be STONITH'ed. #ret -> 255 : ssh command is failed. # else : Maybe command strings in authorized_keys is wrong... return ${ret} } ### # # Main function. # ### case $1 in gethosts) check_hostlist for hostname in ${hostlist} ; do echo "${hostname}" done exit 0 ;; on) # This plugin does only check whether a target node is kdumping or not. exit 1 ;; reset|off) check_hostlist ret=1 h_target=`echo $2 | tr A-Z a-z` for hostname in ${hostlist} do hostname=`echo $hostname | tr A-Z a-z` if [ "${hostname}" != "$h_target" ]; then continue fi while [ 1 ] do check_kdump "$2" ret=$? if [ ${ret} -ne 255 ]; then exit ${ret} fi #255 means ssh command itself is failed. #For example, connection failure as if network doesn't start yet #in 2nd kernel on the target node. #So, retry to check after a little while. sleep 1 done done exit ${ret} ;; status) check_hostlist for hostname in ${hostlist} do if ping -w1 -c1 "${hostname}" 2>&1 | grep "unknown host" then exit 1 fi done get_username check_user_existence check_identity_file exit 0 ;; getconfignames) echo "hostlist identity_file" exit 0 ;; getinfo-devid) echo "kdump check STONITH device" exit 0 ;; getinfo-devname) echo "kdump check STONITH external device" exit 0 ;; getinfo-devdescr) echo "ssh-based kdump checker" echo "To check whether a target node is dumping or not." exit 0 ;; getinfo-devurl) echo "kdump -> http://lse.sourceforge.net/kdump/" echo "ssh -> http://openssh.org" exit 0 ;; getinfo-xml) cat << SSHXML Hostlist The list of hosts that the STONITH device controls Identity file's full path for kdump check user The full path of kdump check user's identity file for ssh command. The identity in the specified file have to be restricted to execute only the following command. "test -s /proc/vmcore" Default: kdump check user's default identity file path. NOTE: You can specify kdump check user name in /etc/kdump.conf. The parameter name is "kdump_check_user". Default user is "kdumpchecker". SSHXML exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/libvirt0000644000000000000000000001533712120057602030431 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for a libvirt managed hypervisor (kvm/Xen). # Uses libvirt as a STONITH device to control guest. # # Copyright (c) 2010 Holger Teutsch # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # start a domain libvirt_start() { out=$($VIRSH -c $hypervisor_uri start $domain_id 2>&1) if [ $? -eq 0 ] then ha_log.sh notice "Domain $domain_id was started" return 0 fi $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 | egrep -q '^State:.*(running|idle)|already active' if [ $? -eq 0 ] then ha_log.sh notice "Domain $domain_id is already active" return 0 fi ha_log.sh err "Failed to start domain $domain_id" ha_log.sh err "$out" return 1 } # reboot a domain # return # 0: success # 1: error libvirt_reboot() { local rc out out=$($VIRSH -c $hypervisor_uri reboot $domain_id 2>&1) rc=$? if [ $rc -eq 0 ] then ha_log.sh notice "Domain $domain_id was rebooted" return 0 fi ha_log.sh err "Failed to reboot domain $domain_id (exit code: $rc)" ha_log.sh err "$out" return 1 } # stop a domain # return # 0: success # 1: error # 2: was already stopped libvirt_stop() { out=$($VIRSH -c $hypervisor_uri destroy $domain_id 2>&1) if [ $? -eq 0 ] then ha_log.sh notice "Domain $domain_id was stopped" return 0 fi $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 | egrep -q '^State:.*shut off|not found|not running' if [ $? -eq 0 ] then ha_log.sh notice "Domain $domain_id is already stopped" return 2 fi ha_log.sh err "Failed to stop domain $domain_id" ha_log.sh err "$out" return 1 } # get status of stonith device (*NOT* of the domain). # If we can retrieve some info from the hypervisor # the stonith device is OK. libvirt_status() { out=$($VIRSH -c $hypervisor_uri version 2>&1) if [ $? -eq 0 ] then return 0 fi ha_log.sh err "Failed to get status for $hypervisor_uri" ha_log.sh err "$out" return 1 } # check config and set variables # does not return on error libvirt_check_config() { VIRSH=`which virsh 2>/dev/null` if [ ! -x "$VIRSH" ] then ha_log.sh err "virsh not installed" exit 1 fi if [ -z "$hostlist" -o -z "$hypervisor_uri" ] then ha_log.sh err "hostlist or hypervisor_uri missing; check configuration" exit 1 fi case "$reset_method" in power_cycle|reboot) : ;; *) ha_log.sh err "unrecognized reset_method: $reset_method" exit 1 ;; esac } # set variable domain_id for the host specified as arg libvirt_set_domain_id () { for h in $hostlist do case $h in $1:*) domain_id=`expr $h : '.*:\(.*\)'` return ;; $1) domain_id=$1 return esac done ha_log.sh err "Should never happen: Called for host $1 but $1 is not in $hostlist." exit 1 } libvirt_info() { cat << LVIRTXML List of hostname[:domain_id].. List of controlled hosts: hostname[:domain_id].. The optional domain_id defaults to the hostname. Hypervisor URI URI for connection to the hypervisor. driver[+transport]://[username@][hostlist][:port]/[path][?extraparameters] e.g. qemu+ssh://my_kvm_server.mydomain.my/system (uses ssh for root) xen://my_kvm_server.mydomain.my/ (uses TLS for client) virsh must be installed (e.g. libvir-client package) and access control must be configured for your selected URI. How to reset a guest. A guest reset may be done by a sequence of off and on commands (power_cycle) or by the reboot command. Which method works depend on the hypervisor and guest configuration management. LVIRTXML exit 0 } ############# # Main code # ############# # don't fool yourself when testing with stonith(8) # and transport ssh unset SSH_AUTH_SOCK # support , as a separator as well hostlist=`echo $hostlist| sed -e 's/,/ /g'` reset_method=${reset_method:-"power_cycle"} case $1 in gethosts) hostnames=`echo $hostlist|sed -e 's/:[^ ]*//g'` for h in $hostnames do echo $h done exit 0 ;; on) libvirt_check_config libvirt_set_domain_id $2 libvirt_start exit $? ;; off) libvirt_check_config libvirt_set_domain_id $2 libvirt_stop [ $? = 1 ] && exit 1 exit 0 ;; reset) libvirt_check_config libvirt_set_domain_id $2 if [ "$reset_method" = "power_cycle" ]; then libvirt_stop rc=$? [ $rc = 1 ] && exit 1 sleep 2 libvirt_start else libvirt_reboot fi rc=$? exit $? ;; status) libvirt_check_config libvirt_status exit $? ;; getconfignames) echo "hostlist hypervisor_uri reboot_method" exit 0 ;; getinfo-devid) echo "libvirt STONITH device" exit 0 ;; getinfo-devname) echo "libvirt STONITH external device" exit 0 ;; getinfo-devdescr) echo "libvirt-based host reset for Xen/KVM guest domain through hypervisor" exit 0 ;; getinfo-devurl) echo "http://libvirt.org/uri.html http://linux-ha.org/wiki" exit 0 ;; getinfo-xml) libvirt_info echo 0; ;; *) exit 1 ;; esac # vi:et:ts=4:sw=4 Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/nut0000644000000000000000000002164512120057602027563 0ustar00usergroup00000000000000#!/bin/sh # External STONITH module that uses the NUT daemon to control an external UPS. # See the comments below, and the various NUT man pages, for how this # script works. It should work unchanged with most modern "smart" APC UPSes in # a Redhat/Fedora/RHEL-style distribution with the nut package installed. # Author: William Seligman # License: GPLv2 # As you're designing your UPS and STONITH set-up, it may help to consider that # there can be potentially three computers involved: # 1) the machine running this STONITH module; # 2) the machine being controlled by this STONITH module ($hostname); # 3) the machine that can send commands to the UPS. # On my cluster, all the UPSes have SNMP smartcards, so every host can communicate # with every UPS; in other words, machines (1) and (3) are the same. If your UPSes # are controlled via serial or USB connections, then you might have a # situation in which $hostname is plugged into a UPS, which has a serial connection # to some master "power-control" computer, and can potentially be STONITHed # by any other machine in your cluster. # In general, you'll probably need the nut daemon running on both the hosts (1) and # (3) in the above list. The NUT daemon will also have to run on (2) if you want the # reset command to gracefully reboot $hostname. # The NUT command default locations. In the RHEL-type nut packages, these binaries # are in /usr/bin. RHELUPSCMD="/usr/bin/upscmd" RHELUPSC="/usr/bin/upsc" # Defaults for APC smart UPSes: # Reset = reboot $hostname; this will be a graceful reboot if the host # is running NUT and monitoring $ups. APCRESET="shutdown.return" # Poweroff = turn off $hostname immediately by cutting the power on $ups. # For a graceful shutdown, use shutdown.stayoff instead of load.off, # but it might take a few minutes to shutdown in this way. APCPOWEROFF="load.off" # Poweron = turn on the power to $ups, which will presumably turn on $hostname. # (Did you set $hostname's BIOS to boot up on AC power restore, as opposed to # "last state"?) APCPOWERON="load.on" # Status = returns a short string with the $ups status; OL = on-line, OFF = off-line, etc. APCSTATUSVAR="ups.status" # Stick in the defaults, if needed. if [ -z "${poweron}" ]; then poweron=${APCPOWERON} fi if [ -z "${poweroff}" ]; then poweroff=${APCPOWEROFF} fi if [ -z "${reset}" ]; then reset=${APCRESET} fi if [ -z "${statusvar}" ]; then statusvar=${APCSTATUSVAR} fi if [ -z "${upscmd}" ]; then upscmd=${RHELUPSCMD} fi if [ -z "${upsc}" ]; then upsc=${RHELUPSC} fi # Define the command to fetch the UPS status. STATUSCMD="${upsc} ${ups} ${statusvar}" usage() { echo "Usage: $0 {on|off|reset|status|gethosts|getconfignames|getinfo-devid|getinfo-devname|getinfo-devdescr|getinfo-devurl|getinfo-xml}" } # Can we find the NUT binary? have_nut() { test -x "${upscmd}" } have_upsc() { test -x "${upsc}" } do_nut() { have_nut || { echo "Can't find NUT upscmd command" return 1 } if [ -z "${username}" -o -z "${password}" -o -z "${ups}" ]; then echo "username, password or ups name missing; check configuration" return 1 fi # Execute the command given in argument 1. ${upscmd} -u ${username} -p ${password} ${ups} ${1} || { echo "error executing nut command" return 1 } } case ${1} in gethosts) echo ${hostname} exit 0 ;; on) result=1 do_nut "${poweron}" result=$? exit ${result} ;; off) result=1 do_nut "${poweroff}" result=$? exit ${result} ;; reset) result=1 do_nut "${reset}" result=$? exit $result ;; status) have_upsc || { echo "Can't find NUT upsc command" exit 1 } ${STATUSCMD} exit $? ;; getconfignames) echo "hostname ups username password poweron poweroff reset statusvar upscmd upsc" exit 0 ;; getinfo-devid) echo "NUT STONITH device" exit 0 ;; getinfo-devname) echo "NUT STONITH external device" exit 0 ;; getinfo-devdescr) echo "A STONITH device based on NUT (Network UPS Tools)." echo " " echo "For this STONITH script to work, the following conditions have" echo "to be met:" echo " " echo "- NUT has to be installed on both the host running this script" echo " and the host that controls the UPS (on RHEL systems, NUT is" echo " in packages nut and nut-client) and the nut daemon services" echo " (normally called the ups or upsd service) must be running" echo " on both systems." echo " " echo "- The UPS name has to be defined in ups.conf on the host" echo " that controls the UPS." echo " " echo "- The username/password to access the UPS must be defined in" echo " upsd.users on the host that controls the UPS, with the instcmds" echo " for poweron, poweroff, and reset allowed." echo " " echo "- The host that is running this script must be allowed access" echo " via upsd.conf and upsd.users on the host the controls the UPS." echo " " echo "On RHEL systems, the files listed above are in /etc/ups." echo " " echo "The defaults will probably work with APC UPS devices. It might" echo "work on others; 'upscmd -l (ups)' and 'upsc (ups)' will list" echo "the commands and variables, and you can change the values" echo "for poweron, poweroff, reset, and statusvar to suit your UPS." echo "Change upscmd and upsc if your NUT binaries are not in /usr/bin." exit 0 ;; getinfo-devurl) echo "http://www.networkupstools.org/" exit 0 ;; getinfo-xml) cat << nutXML Hostname The name of the host to be managed by this STONITH device. The nut daemon must be running on the host controllng the UPS _and_ on the host running this script; this script does not start/stop the daemons for you. UPS name The name of the UPS as defined in ups.conf on the host controlling the UPS. The format for this option is upsname[@controlhost[:port]]. The default controlhost is "localhost". Username The username used for accessing the UPS. This is defined in upsd.conf on the host controlling the UPS. Password The password used for logging in to the UPS for the host controlling the UPS, as defined in upsd.conf on that host. UPS Power On command The NUT hardware command to turn on the UPS. The default should work for most "smart" APC UPSes. For a list of commands that your UPS can support, type 'upscmd -l (ups)' on the command line. UPS Power Off command The NUT hardware command to turn off the UPS. On most APC "smart" UPSes, the command shutdown.stayoff will result in a graceful shutdown, provided the host is running the nut daemon, but this might take a few minutes; load.off will cut the power immediately. For a list of commands that your UPS can support, type 'upscmd -l (ups)' on the command line. UPS Reset command The NUT hardware command to reset the host. On most APC "smart" UPSes, the command shutdown.return will result in a graceful shutdown, with power restored after perhaps a short interval. For a list of commands that your UPS can support, type 'upscmd -l (ups)' on the command line. UPS Status variable The NUT variable that returns the status of the UPS. On APC UPSes, the value of ups.status will be "OL" if the UPS is "on-line." For a list of variables that your UPS supports, type 'upsc (ups)' on the command line. upscmd binary location The full path to the NUT binary command 'upscmd'. On RHEL systems with the nut RPM installed, this location is /usr/bin/upscmd. upsc binary location The full path to the NUT binary command 'upsc'. On RHEL systems with the nut RPM installed, this location is /usr/bin/upsc. nutXML exit 0 ;; *) usage exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/rackpdu0000644000000000000000000001637512120057602030412 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for APC Switched Rack PDU # # Copyright (c) 2008 Sergey Maznichenko # Version 1.2 # # See http://www.it-consultant.su/rackpdu # for additional information # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # SWITCH_ON="1" SWITCH_OFF="2" SWITCH_RESET="3" DEFAULT_NAMES_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2" DEFAULT_COMMAND_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4" if [ -z "$oid" ]; then oid=$DEFAULT_COMMAND_OID fi if [ -z "$names_oid" ]; then names_oid=$DEFAULT_NAMES_OID fi if [ -z "$outlet_config" ]; then outlet_config="none" fi GetOutletNumber() { local nodename=$1 if [ "$outlet_config" != "none" ]; then # Get outlet number from file if [ -f "$outlet_config" ]; then local outlet_num=`grep $nodename $outlet_config | tr -d ' ' | cut -f2 -d'='` if [ -z "$outlet_num" ]; then ha_log.sh err "Outlet number not found for node $nodename. Check configuration file $outlet_config" return 0 fi return $outlet_num else ha_log.sh err "File $outlet_config not found." return 0 fi else # Get outlet number from device local outlet_num=1 local snmp_result snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1` if [ $? -ne 0 ]; then ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result" return 0 fi local names names=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '` for name in $names; do if [ "$name" != "$nodename" ]; then local outlet_num=`expr $outlet_num + 1` continue fi return $outlet_num done ha_log.sh err "Outlet number not found for node $nodename. Result: $snmp_result" return 0 fi } SendCommand() { local host=$1 local command=$2 GetOutletNumber $host local outlet=$? if [ $outlet -gt 0 ]; then local set_result set_result=`snmpset -v1 -c $community $pduip $oid.$outlet i $command 2>&1` if [ $? -ne 0 ]; then ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command failed. Result: $set_result" return 1 fi if echo "$set_result" | grep -qs "Timeout"; then ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command timed out. Result: $set_result" return 1 fi return 0 else return 1 fi } hostlist=`echo $hostlist | tr ',' ' '` incommand=$1 innode=$2 case $incommand in gethosts) if [ "$hostlist" = "AUTO" ]; then snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1` if [ $? -ne 0 ]; then ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result" exit 1 fi if echo "$snmp_result" | grep -qs "Timeout"; then ha_log.sh err "snmpwalk $community $pduip $names_oid timed out. Result: $snmp_result" exit 1 else hostlist=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '` fi fi for h in $hostlist ; do echo $h done exit 0 ;; on) if SendCommand $innode $SWITCH_ON then exit 0 else exit 1 fi ;; off) if SendCommand $innode $SWITCH_OFF then exit 0 else exit 1 fi ;; reset) if SendCommand $innode $SWITCH_RESET then exit 0 else exit 1 fi ;; status) if [ -z "$pduip" ]; then exit 1 fi if ping -w1 -c1 $pduip >/dev/null 2>&1; then exit 0 else exit 1 fi ;; getconfignames) echo "hostlist pduip community" exit 0 ;; getinfo-devid) echo "rackpdu STONITH device" exit 0 ;; getinfo-devname) echo "rackpdu STONITH external device" exit 0 ;; getinfo-devdescr) echo "APC Switched Rack PDU" exit 0 ;; getinfo-devurl) echo "http://www.apcc.com/products/family/index.cfm?id=30" exit 0 ;; getinfo-xml) cat << PDUXML Hostlist The list of hosts that the STONITH device controls (comma or space separated). If you set value of this parameter to AUTO, list of hosts will be get from Rack PDU device. Name or IP address of Rack PDU device. Name or IP address of Rack PDU device. Name of write community. Name of write community. The OID without the outlet number. The SNMP OID for the PDU. minus the outlet number. Try .1.3.6.1.4.1.318.1.1.12.3.3.1.1.4 (default value) or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/ Varies on different APC hardware and firmware. Warning! No dot at the end of OID The OID for getting names of outlets. The SNMP OID for getting names of outlets. It is required to recognize outlet number by nodename. Try ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2" (default value) or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/ Names of nodes must be equal names of outlets, in other way use outlet_config parameter. If you set 'names_oid' parameter then parameter outlet_config must not be use. Varies on different APC hardware and firmware. Warning! No dot at the end of OID Configuration file. Other way to recognize outlet number by nodename. Configuration file. Other way to recognize outlet number by nodename. Configuration file which contains node_name=outlet_number strings. Example: server1=1 server2=2 If you use outlet_config parameter then names_oid parameter can have any value and it is not uses. PDUXML exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/riloe0000644000000000000000000003755412120057602030075 0ustar00usergroup00000000000000#!/usr/bin/env python # # Stonith module for RILOE Stonith device # # Copyright (c) 2004 Alain St-Denis # # Modified by Alan Robertson for STONITH external compatibility. # # Extended and merged by Tijl Van den broeck # with ilo-v2 script from Guy Coates # # Cleanup by Andrew Beekhof # # Rewritten by Dejan Muhamedagic # Now, the plugin actually reads replies from iLO. # # Extended by Jochen Roeder # to enable access via proxies # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import sys import os import socket import subprocess import xml.dom.minidom import httplib import time import re def log_msg(level,msg): subprocess.call("ha_log.sh %s '%s'" % (level,msg), shell=True) def my_err(msg): log_msg("err", msg) def my_warn(msg): log_msg("warn", msg) def my_debug(msg): log_msg("debug", msg) def fatal(msg): my_err(msg) sys.exit(1) argv = sys.argv try: cmd = argv[1] except IndexError: my_err("Not enough arguments") sys.exit(1) legacy_RI_HOST = os.environ.get('RI_HOST', '') legacy_RI_HOSTRI = os.environ.get('RI_HOSTRI', '') legacy_RI_LOGIN = os.environ.get('RI_LOGIN', 'Administrator') legacy_RI_PASSWORD = os.environ.get('RI_PASSWORD', '') reset_ok = os.environ.get('ilo_can_reset', '0') ilo_protocol = os.environ.get('ilo_protocol', '1.2') power_method = os.environ.get('ilo_powerdown_method', 'power') realhost = os.environ.get('hostlist', legacy_RI_HOST) rihost = os.environ.get('ilo_hostname', legacy_RI_HOSTRI) ilouser = os.environ.get('ilo_user', legacy_RI_LOGIN) ilopass = os.environ.get('ilo_password', legacy_RI_PASSWORD) iloproxyhost = os.environ.get('ilo_proxyhost', '') try: iloproxyport = int(os.environ.get('ilo_proxyport', 3128)) except ValueError: my_err("ilo_proxyport is not a number") sys.exit(1) xmlinfo = ''' ilo target hostname Contains the hostname that the ilo controls ilo device hostname The hostname of the ilo device ilo user The user for connecting to the ilo device password The password for the ilo device user Device can reset Does the ILO device support RESET commands (hint: older ones cannot) ILO Protocol Protocol version supported by the ILO device. Known supported versions: 1.2, 2.0 Power down method The method to powerdown the host in question. * button - Emulate holding down the power button * power - Emulate turning off the machines power NB: A button request takes around 20 seconds. The power method about half a minute. Proxy hostname proxy hostname if required to access ILO board Proxy port proxy port if required to access ILO board parameter will be ignored if proxy hostname is not set ''' info = { 'getinfo-devid': 'iLO2', 'getinfo-devname': 'ilo2 ' + rihost, 'getinfo-devdescr': 'HP/COMPAQ iLO2 STONITH device', 'getinfo-devurl': 'http://www.hp.com/', 'gethosts': realhost, 'getinfo-xml': xmlinfo } if cmd in info: print info[cmd] sys.exit(0) if cmd == 'getconfignames': for arg in [ "hostlist", "ilo_hostname", "ilo_user", "ilo_password", "ilo_can_reset", "ilo_protocol", "ilo_powerdown_method", "ilo_proxyhost", "ilo_proxyport"]: print arg sys.exit(0) if not rihost: fatal("ILO device hostname not specified") if not realhost: fatal("Host controlled by this ILO device not specified") if not power_method in ("power","button"): my_err('unknown power method %s, setting to "power"') power_method = "power" # XML elements E_RIBCL = "RIBCL" E_LOGIN = "LOGIN" E_SERVER_INFO = "SERVER_INFO" # power mgmt methods E_RESET = "RESET_SERVER" # error if powered off E_COLD_BOOT = "COLD_BOOT_SERVER" # error if powered off E_WARM_BOOT = "WARM_BOOT_SERVER" # error if powered off E_PRESS_BUTTON = "PRESS_PWR_BTN" E_HOLD_BUTTON = "HOLD_PWR_BTN" # get/set status elements E_SET_POWER = "SET_HOST_POWER" E_GET_PSTATUS = "GET_HOST_POWER_STATUS" # whatever this means, but we have to use it to get good XML E_LOCFG = "LOCFG" LOCFG_VER = '2.21' # attributes A_VERSION = "VERSION" # ilo_protocol A_USER = "USER_LOGIN" A_PWD = "PASSWORD" A_MODE = "MODE" # info mode (read or write) A_POWER_SW = "HOST_POWER" # "Y" or "N" A_POWER_STATE = "HOST_POWER" # "ON" or "OFF" def new_power_req(tag, name = None, value = None): ''' Create a new RIBCL request (as XML). ''' my_debug("creating power request: %s,%s,%s"%(tag,name,value)) doc = xml.dom.minidom.Document() locfg = doc.createElement(E_LOCFG) locfg.setAttribute(A_VERSION,LOCFG_VER) ribcl = doc.createElement(E_RIBCL) ribcl.setAttribute(A_VERSION,ilo_protocol) login = doc.createElement(E_LOGIN) login.setAttribute(A_USER,ilouser) login.setAttribute(A_PWD,ilopass) serv_info = doc.createElement(E_SERVER_INFO) # read or write, it doesn't really matter, i.e. even if we # say "write" that doesn't mean we can't read serv_info.setAttribute(A_MODE,"write") doc.appendChild(locfg) locfg.appendChild(ribcl) ribcl.appendChild(login) login.appendChild(serv_info) el_node = doc.createElement(tag) if name: el_node.setAttribute(name,value) serv_info.appendChild(el_node) s = doc.toprettyxml() doc.unlink() # work around an iLO bug: last line containing "" # produces a syntax error lines = s.split('\n') return '\n'.join(lines[:-2]) E_RESPONSE = "RESPONSE" E_HOST_POWER = "GET_HOST_POWER" A_STATUS = "STATUS" # documentation mentions both; better safe than sorry A_MSG = "MSG" A_MSG2 = "MESSAGE" def is_element(xmlnode): return xmlnode.nodeType == xmlnode.ELEMENT_NODE def read_resp(node): ''' Check if the RESPONSE XML is OK. ''' msg = "" str_status = "" for attr in node.attributes.keys(): if attr == A_STATUS: str_status = node.getAttribute(attr) elif attr == A_MSG: msg = node.getAttribute(attr) elif attr == A_MSG2: msg = node.getAttribute(attr) else: my_warn("unexpected attribute %s in %s" % (attr,E_RESPONSE)) if not str_status: my_err("no status in response") return -1 try: status = int(str_status,16) except ValueError: my_err("unexpected status %s in response" % str_status) return -1 if status != 0: my_err("%s (rc: %s)"%(msg,str_status)) return -1 return 0 def read_power(node): ''' Read the power from the XML node. Set the global power variable correspondingly. ''' global power for attr in node.attributes.keys(): if attr == A_POWER_STATE: power_state = node.getAttribute(attr).upper() else: my_warn("unexpected attribute %s in %s" % (attr,node.tagName)) if not power_state: my_err("no %s attribute in %s" % (A_POWER_STATE,node.tagName)) return -1 if power_state not in ("ON","OFF"): my_err("unexpected value for %s: %s" % (A_POWER_STATE,power_state)) return -1 power = (power_state == "ON") my_debug("Host has power: %s"%power) return 0 el_parsers = { E_RESPONSE:read_resp, E_HOST_POWER:read_power } def proc_resp(doc): ''' Process one iLO reply. Real work is done in el_parsers. ''' ribcl = doc.childNodes[0] if not is_element(ribcl) or ribcl.tagName != E_RIBCL: my_err("unexpected top element in response") return -1 for child in ribcl.childNodes: if not is_element(child): continue if child.tagName in el_parsers: rc = el_parsers[child.tagName](child) if rc != 0: return -1 else: my_warn("unexpected element in response: %s" % child.toxml()) return 0 def open_ilo(host): # open https connection try: if iloproxyhost != "" and iloproxyport != 0: proxy=socket.socket(socket.AF_INET,socket.SOCK_STREAM) proxy.connect((iloproxyhost, iloproxyport)) proxy_connect='CONNECT %s:%s HTTP/1.1\r\n'%(host,443) user_agent='User-Agent: python\r\n' proxy_pieces=proxy_connect+user_agent+'\r\n' proxy.sendall(proxy_pieces) response=proxy.recv(8192) status=response.split()[1] if status!=str(200): fatal("Error status=: %s" %(response)) import ssl sock = ssl.wrap_socket(proxy) h=httplib.HTTPConnection('localhost') h.sock=sock return h else: return httplib.HTTPSConnection(host) except socket.gaierror, msg: fatal("%s: %s" %(msg,host)) except socket.sslerror, msg: fatal("%s for %s" %(msg,host)) except socket.error, msg: fatal("%s while talking to %s" %(msg,host)) except ImportError, msg: fatal("ssl support missing (%s)" %msg) def send_request(req,proc_f): ''' 1. After every request, the iLO closes the connection. 2. For every request, there are multiple replies. Each reply is an XML document. Most of replies are just a kind of (verbose) XML "OK". ''' t_begin = time.time() c = open_ilo(rihost) try: c.send(req+'\r\n') except socket.error, msg: fatal("%s, while talking to %s" %(msg,rihost)) t_end = time.time() my_debug("request sent in %0.2f s" % ((t_end-t_begin))) t_begin = time.time() result = [] while True: try: reply = c.sock.recv(1024) if not reply: break result.append(reply) except socket.error, msg: if msg[0] == 6: # connection closed break my_err("%s, while talking to %s" %(msg,rihost)) return -1 c.close() t_end = time.time() if not result: fatal("no response from %s within %0.2f s"%(rihost,(t_end-t_begin))) for reply in result: # work around the iLO bug, i.e. element RIBCL closed twice if re.search("", reply): reply = re.sub("<(RIBCL.*)/>", r"<\1>", reply) try: doc = xml.dom.minidom.parseString(reply) except xml.parsers.expat.ExpatError,msg: fatal("malformed response: %s\n%s"%(msg,reply)) rc = proc_f(doc) doc.unlink() if rc != 0: break my_debug("iLO processed request (rc=%d) in %0.2f s" % (rc,(t_end-t_begin))) return rc def manage_power(cmd): ''' Before trying to send a request we have to check the power state. ''' rc = 0 req = '' # it won't do to turn it on if it's already on! if cmd == "on" and not power: req = new_power_req(E_SET_POWER,A_POWER_SW,"Y") # also to turn it off if it's already off elif cmd == "off" and power: req = new_power_req(E_SET_POWER,A_POWER_SW,"N") elif cmd == "cold_boot" and power: req = new_power_req(E_COLD_BOOT) elif cmd == "warm_boot" and power: req = new_power_req(E_WARM_BOOT) elif cmd == "reset": if power: req = new_power_req(E_RESET) # reset doesn't work if the host's off else: req = new_power_req(E_SET_POWER,A_POWER_SW,"Y") if req: rc = send_request(req,proc_resp) return rc def power_on(): ''' Update the power variable without checking the power state. The iLO is slow at times to report the actual power state, so we assume that it changed if the request succeeded. ''' rc = manage_power("on") if rc == 0: global power power = True return rc def power_off(): rc = manage_power("off") if rc == 0: global power power = False return rc def cold_boot(): rc = manage_power("cold_boot") return rc def warm_boot(): rc = manage_power("warm_boot") return rc def reset(): rc = manage_power("reset") if rc == 0: global power power = True return rc def hold_button(): ''' Hold the power button. Got this error message when tried without the TOGGLE attribute: Command without TOGGLE="Yes" attribute is ignored when host power is off. (rc: 0x0054) Didn't find any documentation about TOGGLE. ''' if power: req = new_power_req(E_HOLD_BUTTON) else: req = new_power_req(E_HOLD_BUTTON,"TOGGLE","Yes") rc = send_request(req,proc_resp) return rc def read_power_state(): req = new_power_req(E_GET_PSTATUS) rc = send_request(req,proc_resp) return rc def reset_power(): ''' Three methods to reset: - hold power button - reset (only if host has power and user said that reset is ok) - power off/on ''' do_power_on = False if power_method == 'button': rc = hold_button() elif reset_ok != '0': if power: return reset() else: return power_on() else: do_power_on = True rc = power_off() if rc == 0: rc = read_power_state() if do_power_on: while rc == 0 and power: # wait for the power state to go off time.sleep(5) rc = read_power_state() if rc == 0 and do_power_on and not power: rc = power_on() return rc # track state of host power power = -1 todo = { 'reset':reset_power, 'on':power_on, 'off':power_off, 'cold':cold_boot, 'warm':warm_boot, 'status':lambda: 0 # just return 0, we already read the state } rc = read_power_state() if rc == 0: if cmd in todo: rc = todo[cmd]() else: fatal('Invalid command: %s' % cmd) if rc != 0: fatal("request failed") sys.exit(rc) # vi:ts=4:sw=4:et: Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/ssh.in0000644000000000000000000001055712120057602030157 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for ssh. # # Copyright (c) 2004 SUSE LINUX AG - Lars Marowsky-Bree # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n -l root" #SSH_COMMAND="@SSH@ -q -x -n -l root" REBOOT_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1" # Warning: If you select this poweroff command, it'll physically # power-off the machine, and quite a number of systems won't be remotely # revivable. # TODO: Probably should touch a file on the server instead to just # prevent heartbeat et al from being started after the reboot. # POWEROFF_COMMAND="echo 'sleep 2; /sbin/poweroff -nf' | SHELL=/bin/sh at now >/dev/null 2>&1" POWEROFF_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1" # Rewrite the hostlist to accept "," as a delimeter for hostnames too. hostlist=`echo $hostlist | tr ',' ' '` is_host_up() { for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 do if ping -w1 -c1 "$1" >/dev/null 2>&1 then sleep 1 else return 1 fi done return 0 } case $1 in gethosts) for h in $hostlist ; do echo $h done exit 0 ;; on) # Can't really be implemented because ssh cannot power on a system # when it is powered off. exit 1 ;; off) # Shouldn't really be implemented because if ssh cannot power on a # system, it shouldn't be allowed to power it off. exit 1 ;; reset) h_target=`echo $2 | tr A-Z a-z` for h in $hostlist do h=`echo $h | tr A-Z a-z` [ "$h" != "$h_target" ] && continue if case ${livedangerously} in [Yy]*) is_host_up $h;; *) true;; esac then $SSH_COMMAND "$2" "$REBOOT_COMMAND" # Good thing this is only for testing... if is_host_up $h then exit 1 else exit 0 fi else # well... Let's call it successful, after all this is only for testing... exit 0 fi done exit 1 ;; status) if [ -z "$hostlist" ] then exit 1 fi for h in $hostlist do if ping -w1 -c1 "$h" 2>&1 | grep "unknown host" then exit 1 fi done exit 0 ;; getconfignames) echo "hostlist" exit 0 ;; getinfo-devid) echo "ssh STONITH device" exit 0 ;; getinfo-devname) echo "ssh STONITH external device" exit 0 ;; getinfo-devdescr) echo "ssh-based host reset" echo "Fine for testing, but not suitable for production!" echo "Only reboot action supported, no poweroff, and, surprisingly enough, no poweron." exit 0 ;; getinfo-devurl) echo "http://openssh.org" exit 0 ;; getinfo-xml) cat << SSHXML Hostlist The list of hosts that the STONITH device controls Live Dangerously!! Set to "yes" if you want to risk your system's integrity. Of course, since this plugin isn't for production, using it in production at all is a bad idea. On the other hand, setting this parameter to yes makes it an even worse idea. Viva la Vida Loca! SSHXML exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/vcenter0000755000000000000000000002204212120057602030416 0ustar00usergroup00000000000000#!/usr/bin/env perl # # External STONITH module for VMWare vCenter/ESX # # Author: Nhan Ngo Dinh # License: GNU General Public License (GPL) # require 5.010; use strict; use warnings; sub dielog { my $msg = "["; $msg .= "$ARGV[0]" if defined($ARGV[0]); $msg .= " $ARGV[1]" if defined($ARGV[1]); $msg .= "]"; ( $_ ) = @_; $msg .= " $_"; system("ha_log.sh", "err", "$msg"); die(); } # Define command groups my @configCommands = qw{getconfignames getinfo-devid getinfo-devname getinfo-devdescr getinfo-devurl getinfo-xml}; my @actionCommands = qw{reset on off}; my @netCommands = (@actionCommands, qw{status gethosts listvms}); # Process command line arguments my $command = $ARGV[0] || dielog("No command specified\n"); # Command belongs to the group of commands that do not require any connection to VMware vCenter if ($command ~~ @configCommands) { if ($command eq "getconfignames") { print "VI_SERVER\nVI_PORTNUMBER\nVI_PROTOCOL\nVI_SERVICEPATH\nVI_CREDSTORE\nHOSTLIST\nRESETPOWERON\n"; } elsif ($command eq "getinfo-devid") { print "VMware vCenter STONITH device\n"; } elsif ($command eq "getinfo-devname") { print "VMware vCenter STONITH device\n"; } elsif ($command eq "getinfo-devdescr") { print "VMWare vCenter STONITH device\n"; } elsif ($command eq "getinfo-devurl") { print "http://www.vmware.com/\n"; } elsif ($command eq "getinfo-xml") { print q{ List of hosts and virtual machines (required) The list of hosts that the VMware vCenter STONITH device controls. Syntax is: hostname1[=VirtualMachineName1];hostname2[=VirtualMachineName2] NOTE: omit =VirtualMachineName if hostname and virtual machine names are identical Example: cluster1=VMCL1;cluster2=VMCL2 VMware vCenter address The VMware vCenter address VMware vCenter protocol The VMware vCenter protocol VMware vCenter port number The VMware vCenter port number VMware vCenter service path The VMware vCenter services path VMware vCenter credentials store file VMware vCenter credentials store file PowerOnVM on reset Enable/disable a PowerOnVM on reset when the target virtual machine is off Allowed values: 0, 1 } . "\n"; } else { dielog("Invalid command specified: $command\n"); } } # Command belongs to the group of commands that require connecting to VMware vCenter elsif ($command ~~ @netCommands) { eval { require VMware::VIRuntime; } or dielog("Missing perl module VMware::VIRuntime. Download and install 'VMware Infrastructure (VI) Perl Toolkit', available at http://www.vmware.com/support/developer/viperltoolkit/ \n"); # A valid VI_CREDSTORE is required to avoid interactive prompt ( exists $ENV{'VI_CREDSTORE'} ) || dielog("VI_CREDSTORE not specified\n"); # HOSTLIST is mandatory exists $ENV{'HOSTLIST'} || dielog("HOSTLIST not specified\n"); # Parse HOSTLIST to %host_to_vm and %vm_to_host my @hostlist = split(';', $ENV{'HOSTLIST'}); my %host_to_vm = (); my %vm_to_host = (); foreach my $host (@hostlist) { my @config = split(/=/, $host); my $key = $config[0]; my $value = $config[1]; if (!defined($value)) { $value = $config[0]; } $host_to_vm{$key} = $value; $vm_to_host{(lc $value)} = $key; } eval { # VI API: reads options from the environment variables into appropriate data structures for validation. Opts::parse(); # VI API: ensures that input values from environment variable are complete, consistent and valid. Opts::validate(); # VI API: establishes a session with the VirtualCenter Management Server or ESX Server Web service Util::connect(); }; if ($@) { # This is just a placeholder for any error handling procedure dielog($@); } # Command belongs to the group of commands that performs actions on Virtual Machines if ($command ~~ @actionCommands) { my $targetHost = $ARGV[1] || dielog("No target specified\n"); # Require that specified target host exists in the specified HOSTLIST if (exists $host_to_vm{$targetHost}) { my $vm; my $esx; eval { # VI API: searches the inventory tree for a VirtualMachine managed entity whose name matches # the name of the virtual machine assigned to the target host in HOSTLIST $vm = Vim::find_entity_view(view_type => "VirtualMachine", filter => { name => qr/^\Q$host_to_vm{$targetHost}\E/i }); if (!defined $vm) { dielog("Machine $targetHost was not found"); } # VI API: retrieves the properties of the managed object reference runtime.host of the VirtualMachine # managed entity obtained by the previous command # NOTE: This is essentially a workaround to vSphere Perl SDK # to allow pointing to the right HostSystem. This is probably # done by changing the current HostSystem in the Web Service # session context. WARNING: Do not use the same session for any # other concurrent operation. $esx = Vim::get_view(mo_ref => $vm->{"runtime"}{"host"})->name; }; if ($@) { if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); } dielog($@); } my $powerState = $vm->get_property('runtime.powerState')->val; if ($powerState eq "suspended") { # This implementation assumes that suspending a cluster node can cause # severe failures on shared resources, thus any failover operation should # be blocked. dielog("Machine $esx:$vm->{'name'} is in a suspended state\n"); } eval { if ($command eq "reset") { if ($powerState eq "poweredOn") { $vm->ResetVM(); system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been reset"); } else { system("ha_log.sh", "warn", "Tried to ResetVM $esx:$vm->{'name'} that was $powerState"); # Start a virtual machine on reset only if explicitly allowed by RESETPOWERON if ($powerState eq "poweredOff" && (! exists $ENV{'RESETPOWERON'} || $ENV{'RESETPOWERON'} ne 0)) { $vm->PowerOnVM(); system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on"); } else { dielog("Could not complete $esx:$vm->{'name'} power cycle"); } } } elsif ($command eq "off") { if ($powerState eq "poweredOn") { $vm->PowerOffVM(); system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered off"); } else { system("ha_log.sh", "warn", "Tried to PowerOffVM $esx:$vm->{'name'} that was $powerState"); } } elsif ($command eq "on") { if ($powerState eq "poweredOff") { $vm->PowerOnVM(); system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on"); } else { system("ha_log.sh", "warn", "Tried to PowerOnVM $esx:$vm->{'name'} that was $powerState"); } } else { dielog("Invalid command specified: $command\n"); } }; if ($@) { if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); } dielog($@); } } else { dielog("Invalid target specified\n"); } } else { # Command belongs to the group of commands that lookup the status of VMware vCenter and/or virtual machines if ($command eq "status") { eval { # VI API: Searches the inventory tree for all VirtualMachine managed objects my $vms = Vim::find_entity_views(view_type => "VirtualMachine"); }; if ($@) { if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); } dielog($@); } } elsif ($command eq "gethosts") { foreach my $key (keys(%host_to_vm)) { print "$key \n"; } } elsif ($command eq "listvms") { eval { # VI API: Searches the inventory tree for all VirtualMachine managed objects my $vms = Vim::find_entity_views(view_type => "VirtualMachine"); if (defined $vms) { printf(STDERR "%-50s %-20s\n", "VM Name", "Power state"); print STDERR "-" x 70 . "\n"; foreach my $vm (@$vms) { my $powerState = $vm->get_property('runtime.powerState')->val; printf("%-50s %-20s\n", $vm->{name}, $powerState); } } }; } else { dielog("Invalid command specified: $command\n"); } } eval { Util::disconnect(); }; if ($@) { # This is just a placeholder for any error handling procedure dielog($@); } } else { dielog("Invalid command specified: $command\n"); } exit(0); Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/vmware0000644000000000000000000001176512120057602030260 0ustar00usergroup00000000000000#!/usr/bin/perl # External STONITH module for VMWare Server Guests # # Copyright (c) 2004 SUSE LINUX AG - Andrew Beekhof # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # sub supply_default { my $name = $_[0]; my $value = $_[1]; if ( defined $ENV{$name} ) { #print "Set: $name=$ENV{$name}\n"; } else { $ENV{$name} = $value; #print "Default: $name=$ENV{$name}\n"; } } sub vmware_command { my $config = $_[0]; my $action = $_[1]; my @lines; my $device = $ENV{'device_host'}; if ( $device =~ /localhost/ ) { @lines = readpipe "vmware-cmd $config $action"; } else { @lines = readpipe "ssh $device \"vmware-cmd \\\"$config\\\" $action\""; } #print @lines; return @lines; } sub is_host_active { my $config = config_for_host($_[0]); my @lines = vmware_command($config, "getstate"); foreach $line (@lines) { if ( $line =~ /getstate.* = on/ ) { return 1; } } return 0; } sub supported_hosts { my $line; my @lines; if ( defined $ENV{'host_map'} ) { @lines = split(/;/, $ENV{'host_map'}); foreach $line (@lines){ @config = split(/=/, $line); $host = $config[0]; if ( is_host_active($host) == 1 ) { print "$host\n"; } } } else { @lines = vmware_command("-l"); foreach $line (@lines){ my @elements = split(/\//, $line); $host = $elements[$#elements-1]; if ( is_host_active($host) == 1 ) { print "$host\n"; } } } } sub config_for_host { my $line; my @lines; my $target = $_[0]; if ( defined $ENV{'host_map'} ) { @lines = split(/;/, $ENV{'host_map'}); foreach $line (@lines){ if ( $line =~ /^$target=/ ) { @config = split(/=/, $line); return $config[1]; } } } else { @lines = vmware_command("-l"); foreach $line (@lines){ if ( $line =~ /\/$target\// ) { chop($line); return $line; } } } } $command = $ARGV[0]; if ( defined $ARGV[1] ) { $targetHost = $ARGV[1]; } supply_default("device_host", "localhost"); if ( $command =~ /^gethosts$/ ) { supported_hosts; } elsif ( $command =~ /^getconfignames$/ ) { print "device_host\n"; } elsif ( $command =~ /^getinfo-devid$/ ) { print "VMware Server STONITH device\n"; } elsif ( $command =~ /^getinfo-devname$/ ) { print "VMware Server STONITH device\n"; } elsif ( $command =~ /^getinfo-devdescr$/ ) { print "VMware Server STONITH device\n"; } elsif ( $command =~ /^getinfo-devurl$/ ) { print "http://www.vmware.com/"; } elsif ( $command =~ /^on$/ ) { $config = config_for_host($targetHost); my @lines = vmware_command($config, "start hard"); print @lines; } elsif ( $command =~ /^off$/ ) { $config = config_for_host($targetHost); my @lines = vmware_command($config, "stop hard"); print @lines; } elsif ( $command =~ /^reset$/ ) { $config = config_for_host($targetHost); my @lines = vmware_command($config, "reset hard"); print @lines; } elsif ( $command =~ /^status$/ ) { my $rc = 7; my $device = $ENV{'device_host'}; if ( $device =~ /localhost/ ) { $rc = 0; # TODO: Check for the vmware process print "Local version: always running\n"; } else { print "Remote version: running ping\n"; @lines = readpipe "ping -c1 $device"; print @lines; foreach $line ( @lines ) { if ( $line =~ /0% packet loss/ ) { $rc = 0; last; } } } exit($rc); } elsif ( $command =~ /^getinfo-xml$/ ) { $metadata = < Host Map A mapping of hostnames to config paths supported by this device. Eg. host1=/config/path/1;host2=/config/path/2;host3=/config/path/3; Device Host The machine _hosting_ the virtual machines END print $metadata; } else { print "Command $command: not supported\n"; exit(3); # Not implemented } exit(0); Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/xen00000644000000000000000000001252412120057602027623 0ustar00usergroup00000000000000#!/bin/sh # # External STONITH module for Xen Dom0 through ssh. # # Description: Uses Xen Dom0 Domain as a STONITH device # to control DomUs. # # # Author: Serge Dubrouski (sergeyfd@gmail.com) # Inspired by Lars Marowsky-Bree's external/ssh agent. # # Copyright 2007 Serge Dubrouski # License: GNU General Public License (GPL) # STOP_COMMAND="xm destroy" START_COMMAND="xm create" DUMP_COMMAND="xm dump-core" DEFAULT_XEN_DIR="/etc/xen" SSH_COMMAND="/usr/bin/ssh -q -x -n" # Rewrite the hostlist to accept "," as a delimeter for hostnames too. hostlist=`echo $hostlist | tr ',' ' '` CheckIfDead() { for j in 1 2 3 4 5 do if ! ping -w1 -c1 "$1" >/dev/null 2>&1 then return 0 fi sleep 1 done return 1 } CheckHostList() { if [ "x" = "x$hostlist" ] then ha_log.sh err "hostlist isn't set" exit 1 fi } CheckDom0() { if [ "x" = "x$dom0" ] then ha_log.sh err "dom0 isn't set" exit 1 fi } RunCommand() { CheckHostList CheckDom0 for h in $hostlist do CIFS=$IFS IFS=: read node cfg << -! $h -! IFS=$CIFS if [ "x" = "x$node" ] then ha_log.sh err "Syntax error in host list" exit 1 fi if [ "x" = "x$cfg" ] then cfg="${DEFAULT_XEN_DIR}/${node}.cfg" fi if [ "$node" != "$1" ] then continue fi case $2 in stop) kill_node=`$SSH_COMMAND $dom0 "grep ^[[:space:]]*name $cfg" | cut -f 2 -d '=' | sed -e 's,",,g'` if [ "x" = "x$kill_node" ] then ha_log.sh err "Couldn't find a node name to stop" exit 1 fi if [ "x$run_dump" != "x" ] then #Need to run core dump if [ "x$dump_dir" != "x" ] then #Dump with the specified core file TIMESTAMP=`date +%Y-%m%d-%H%M.%S` DOMAINNAME=`printf "%s" $kill_node` COREFILE=$dump_dir/$TIMESTAMP-$DOMAINNAME.core $SSH_COMMAND $dom0 "(mkdir -p $dump_dir; $DUMP_COMMAND $kill_node $COREFILE) >/dev/null 2>&1" else $SSH_COMMAND $dom0 "$DUMP_COMMAND $kill_node >/dev/null 2>&1" fi fi $SSH_COMMAND $dom0 "(sleep 2; $STOP_COMMAND $kill_node) >/dev/null 2>&1 &" break;; start) $SSH_COMMAND $dom0 "(sleep 2; $START_COMMAND $cfg) >/dev/null 2>&1 &" break;; esac exit 0 done } # Main code case $1 in gethosts) CheckHostList for h in $hostlist ; do CIFS=$IFS IFS=: read node cfg << -! $h -! IFS=$CIFS echo $node done exit 0 ;; on) RunCommand $2 start exit $? ;; off) if RunCommand $2 stop then if CheckIfDead $2 then exit 0 fi fi exit 1 ;; reset) RunCommand $2 stop if CheckIfDead $2 then RunCommand $2 start exit 0 fi exit 1 ;; status) CheckHostList for h in $hostlist do CIFS=$IFS IFS=: read node cfg << -! $h -! IFS=$CIFS echo $node if ping -w1 -c1 "$node" 2>&1 | grep "unknown host" then exit 1 fi done exit 0 ;; getconfignames) echo "hostlist dom0" exit 0 ;; getinfo-devid) echo "xen0 STONITH device" exit 0 ;; getinfo-devname) echo "xen0 STONITH external device" exit 0 ;; getinfo-devdescr) echo "ssh-based host reset for Xen DomU trough Dom0" echo "Fine for testing, but not really suitable for production!" exit 0 ;; getinfo-devurl) echo "http://openssh.org http://www.xensource.com/ http://linux-ha.org/wiki" exit 0 ;; getinfo-xml) cat << SSHXML Hostlist The list of controlled nodes in a format node[:config_file]. For example: "node1:/opt/xen/node1.cfg node2" If config file isn't set it defaults to /etc/xen/{node_name}.cfg Dom0 Name of the Dom0 Xen node. Root user shall be able to ssh to that node. Run dump-core If set plugin will call "xm dump-core" before killing DomU Run dump-core with the specified directory This parameter can indicate the dump destination. Should be set as a full path format, ex.) "/var/log/dump" The above example would dump the core, like; /var/log/dump/2009-0316-1403.37-domU.core SSHXML exit 0 ;; *) exit 1 ;; esac ././@LongLink0000000000000000000000000000015000000000000013225 Lustar00usergroup00000000000000Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helperReusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/xen0-ha-dom0-stonith-hel0000755000000000000000000000247712120057602033313 0ustar00usergroup00000000000000#!/bin/bash # Author: Lars Marowsky-Bree # # Copyright 2008 Lars Marowsky-Bree # License: GNU General Public License (GPL) # This is not an external/stonith plugin by itself, but instead a helper # script which gets installed in Dom0. # TODO: # - Error handling # - How to handle if the DomU resource doesn't exist? # - Does this truly work with split-brain? # - Is the handling of non-existent resources adequate? # ... # Basically: more testing. This is proof-of-concept and works, but deserves # validation. CMD="$1" DOMU="$2" TIMEOUT="$3" # Make sure the timeout is an integer: if [ "0$TIMEOUT" -eq 0 ]; then TIMEOUT=300 fi SetTargetRole() { local new_role="$1" crm_resource -r $DOMU --meta -p target_role -v $new_role local timeout="$TIMEOUT" # We only need to wait for "stopped". if [ "$new_role" != "stopped" ]; then return 0 fi while [ $timeout -gt 0 ]; do local rc crm_resource -W -r $DOMU 2>&1 | grep -q "is NOT running" rc=$? if [ $rc -eq 0 ]; then return 0 fi timeout=$[timeout-1]; sleep 1 done return 1 } case $CMD in on) SetTargetRole started exit $? ;; off) SetTargetRole stopped exit $? ;; reset) SetTargetRole stopped || exit 1 SetTargetRole started exit $? ;; status) exit 0 ;; *) ha_log.sh err "Called with unknown command: $CMD" exit 1 ;; esac exit 1 Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/external/xen0-ha.in0000755000000000000000000000427112120057602030621 0ustar00usergroup00000000000000#!/bin/bash # # This STONITH script integrates a cluster running within DomUs # with the CRM/Pacemaker cluster running in Dom0. # # Author: Lars Marowsky-Bree # Copyright: 2008 Lars Marowsky-Bree # License: GNU General Public License (GPL) # SSH_COMMAND="@SSH@ -q -x -n" HVM_HELPER="@stonith_plugindir@/xen0-ha-dom0-stonith-helper" # Rewrite the hostlist to accept "," as a delimeter for hostnames too. hostlist=`echo $hostlist | tr ',' ' '` # Runs a command on the host, waiting for it to return RunHVMCommand() { $SSH_COMMAND $dom0_cluster_ip "$HVM_HELPER $1 $2 $stop_timeout" } # Main code case $1 in gethosts) echo $hostlist exit 0 ;; on|off|reset|status) RunHVMCommand $1 $2 exit $? ;; getconfignames) echo "hostlist dom0_cluster_ip timeout" exit 0 ;; getinfo-devid) echo "xen0-ha DomU/Dom0 device" exit 0 ;; getinfo-devname) echo "xen0-ha DomU/Dom0 external device" exit 0 ;; getinfo-devdescr) echo "Allows STONITH to control DomUs managed by a CRM/Pacemaker Dom0." echo "Requires Xen + CRM/Pacemaker at both layers." echo "Proof-of-concept code!" exit 0 ;; getinfo-devurl) echo "http://linux-ha.org/wiki/DomUClusters" exit 0 ;; getinfo-xml) cat << SSHXML Hostlist The list of controlled DomUs, separated by whitespace. These must be configured as Xen RA resources with a name with a matching id. For example: "xen-1 xen-2 xen-3" Dom0 cluster ip The cluster IP address associated with Dom0. Root user must be able to ssh to that node. Stop timeout The timeout, in seconds, for which to wait for Dom0 to report that the DomU has been stopped, before aborting with a failure. SSHXML exit 0 ;; *) exit 1 ;; esac Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ibmhmc.c0000644000000000000000000007374512120057602026623 0ustar00usergroup00000000000000/* * Stonith module for IBM Hardware Management Console (HMC) * * Author: Huang Zhen * Support for HMC V4+ added by Dave Blaschke * * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * * This code has been tested in following environment: * * Hardware Management Console (HMC): Release 3, Version 2.4 * - Both FullSystemPartition and LPAR Partition: * - p630 7028-6C4 two LPAR partitions * - p650 7038-6M2 one LPAR partition and FullSystemPartition * * Hardware Management Console (HMC): Version 4, Release 2.1 * - OP720 1000-6CA three LPAR partitions * * Note: Only SSH access to the HMC devices are supported. * * This command would make a nice status command: * * lshmc -r -F ssh * * The following V3 command will get the list of systems we control and their * mode: * * lssyscfg -r sys -F name:mode --all * * 0 indicates full system partition * 255 indicates the system is partitioned * * The following V4 command will get the list of systems we control: * * lssyscfg -r sys -F name * * The following V3 command will get the list of partitions for a given managed * system running partitioned: * * lssyscfg -m managed-system -r lpar -F name --all * * Note that we should probably only consider partitions whose boot mode * is normal (1). (that's my guess, anyway...) * * The following V4 command will get the list of partitions for a given managed * system running partitioned: * * lssyscfg -m managed-system -r lpar -F name * * The following V3 commands provide the reset/on/off actions: * * FULL SYSTEM: * on: chsysstate -m %1 -r sys -o on -n %1 -c full * off: chsysstate -m %1 -r sys -o off -n %1 -c full -b norm * reset:chsysstate -m %1 -r sys -o reset -n %1 -c full -b norm * * Partitioned SYSTEM: * on: chsysstate -m %1 -r lpar -o on -n %2 * off: reset_partition -m %1 -p %2 -t hard * reset:do off action above, followed by on action... * * where %1 is managed-system, %2 is-lpar name * * The following V4 commands provide the reset/on/off actions: * * on: chsysstate -m %1 -r lpar -o on -n %2 -f %3 * off: chsysstate -m %1 -r lpar -o shutdown -n %2 --immed * reset:chsysstate -m %1 -r lpar -o shutdown -n %2 --immed --restart * * where %1 is managed-system, %2 is lpar-name, %3 is profile-name * * Of course, to do all this, we need to track which partition name goes with * which managed system's name, and which systems on the HMC are partitioned * and which ones aren't... */ #include #define DEVICE "IBM HMC" #include "stonith_plugin_common.h" #ifndef SSH_CMD # define SSH_CMD "ssh" #endif #ifndef HMCROOT # define HMCROOT "hscroot" #endif #define PIL_PLUGIN ibmhmc #define PIL_PLUGIN_S "ibmhmc" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #define MAX_HOST_NAME_LEN (256*4) #define MAX_CMD_LEN 2048 #define FULLSYSTEMPARTITION "FullSystemPartition" #define MAX_POWERON_RETRY 10 #define MAX_HMC_NAME_LEN 256 #define ST_MANSYSPAT "managedsyspat" #define NOPASS "nopass" #define STATE_UNKNOWN -1 #define STATE_OFF 0 #define STATE_ON 1 #define STATE_INVALID 2 #define HMCURL "http://publib-b.boulder.ibm.com/redbooks.nsf/RedbookAbstracts"\ "/SG247038.html" static StonithPlugin * ibmhmc_new(const char *); static void ibmhmc_destroy(StonithPlugin *); static const char * ibmhmc_getinfo(StonithPlugin * s, int InfoType); static const char * const * ibmhmc_get_confignames(StonithPlugin* p); static int ibmhmc_status(StonithPlugin * ); static int ibmhmc_reset_req(StonithPlugin * s,int request,const char* host); static char ** ibmhmc_hostlist(StonithPlugin *); static int ibmhmc_set_config(StonithPlugin *, StonithNVpair*); static struct stonith_ops ibmhmcOps = { ibmhmc_new, /* Create new STONITH object */ ibmhmc_destroy, /* Destroy STONITH object */ ibmhmc_getinfo, /* Return STONITH info string */ ibmhmc_get_confignames, /* Return configuration parameters */ ibmhmc_set_config, /* Set configuration */ ibmhmc_status, /* Return STONITH device status */ ibmhmc_reset_req, /* Request a reset */ ibmhmc_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &ibmhmcOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } struct pluginDevice { StonithPlugin sp; const char * pluginid; char * idinfo; char * hmc; GList* hostlist; int hmcver; char * password; char ** mansyspats; }; static const char * pluginid = "HMCDevice-Stonith"; static const char * NOTpluginID = "IBM HMC device has been destroyed"; #include "stonith_config_xml.h" #define XML_MANSYSPAT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_MANSYSPAT \ XML_PARM_SHORTDESC_END #define XML_MANSYSPAT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "White-space delimited list of patterns used to match managed system names; if last character is '*', all names that begin with the pattern are matched" \ XML_PARM_LONGDESC_END #define XML_MANSYSPAT_PARM \ XML_PARAMETER_BEGIN(ST_MANSYSPAT, "string", "0") \ XML_MANSYSPAT_SHORTDESC \ XML_MANSYSPAT_LONGDESC \ XML_PARAMETER_END #define XML_OPTPASSWD_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "Password for " HMCROOT " if passwordless ssh access to HMC has NOT been setup (to do so, it is necessary to create a public/private key pair with empty passphrase - see \"Configure the OpenSSH Client\" in the redbook at " HMCURL " for more details)" \ XML_PARM_LONGDESC_END #define XML_OPTPASSWD_PARM \ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "0") \ XML_PASSWD_SHORTDESC \ XML_OPTPASSWD_LONGDESC \ XML_PARAMETER_END static const char *ibmhmcXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_MANSYSPAT_PARM XML_OPTPASSWD_PARM XML_PARAMETERS_END; static int get_hmc_hostlist(struct pluginDevice* dev); static void free_hmc_hostlist(struct pluginDevice* dev); static int get_hmc_mansyspats(struct pluginDevice* dev, const char* mansyspats); static void free_hmc_mansyspats(struct pluginDevice* dev); static char* do_shell_cmd(const char* cmd, int* status, const char* password); static int check_hmc_status(struct pluginDevice* dev); static int get_num_tokens(char *str); static gboolean pattern_match(char **patterns, char *string); /* static char* do_shell_cmd_fake(const char* cmd, int* status); */ static int ibmhmc_status(StonithPlugin *s) { struct pluginDevice* dev = NULL; if(Debug){ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); dev = (struct pluginDevice*) s; return check_hmc_status(dev); } /* * Return the list of hosts configured for this HMC device */ static char ** ibmhmc_hostlist(StonithPlugin *s) { int j; struct pluginDevice* dev; int numnames = 0; char** ret = NULL; GList* node = NULL; if(Debug){ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); dev = (struct pluginDevice*) s; /* refresh the hostlist */ free_hmc_hostlist(dev); if (S_OK != get_hmc_hostlist(dev)){ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s" , __FUNCTION__); return NULL; } numnames = g_list_length(dev->hostlist); if (numnames < 0) { LOG(PIL_CRIT, "unconfigured stonith object in %s" , __FUNCTION__); return(NULL); } ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { LOG(PIL_CRIT, "out of memory"); return ret; } memset(ret, 0, (numnames+1)*sizeof(char*)); for (node = g_list_first(dev->hostlist), j = 0 ; NULL != node ; j++, node = g_list_next(node)) { char* host = strchr((char*)node->data, '/'); ret[j] = STRDUP(++host); if (ret[j] == NULL) { LOG(PIL_CRIT, "out of memory"); stonith_free_hostlist(ret); return NULL; } g_strdown(ret[j]); } return ret; } static const char * const * ibmhmc_get_confignames(StonithPlugin* p) { static const char * names[] = {ST_IPADDR, NULL}; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } return names; } /* * Reset the given host, and obey the request type. * We should reset without power cycle for the non-partitioned case */ static int ibmhmc_reset_req(StonithPlugin * s, int request, const char * host) { GList* node = NULL; struct pluginDevice* dev = NULL; char off_cmd[MAX_CMD_LEN]; char on_cmd[MAX_CMD_LEN]; char reset_cmd[MAX_CMD_LEN]; gchar** names = NULL; int i; int is_lpar = FALSE; int status; char* pch; char* output = NULL; char state_cmd[MAX_CMD_LEN]; int state = STATE_UNKNOWN; status = 0; if(Debug){ LOG(PIL_DEBUG, "%s: called, host=%s\n", __FUNCTION__, host); } ERRIFWRONGDEV(s,S_OOPS); if (NULL == host) { LOG(PIL_CRIT, "invalid argument to %s", __FUNCTION__); return(S_OOPS); } dev = (struct pluginDevice*) s; for (node = g_list_first(dev->hostlist) ; NULL != node ; node = g_list_next(node)) { if(Debug){ LOG(PIL_DEBUG, "%s: node->data=%s\n" , __FUNCTION__, (char*)node->data); } if ((pch = strchr((char*)node->data, '/')) != NULL && 0 == strcasecmp(++pch, host)) { break; } } if (!node) { LOG(PIL_CRIT , "Host %s is not configured in this STONITH module. " "Please check your configuration information.", host); return (S_OOPS); } names = g_strsplit((char*)node->data, "/", 2); /* names[0] will be the name of managed system */ /* names[1] will be the name of the lpar partition */ if(Debug){ LOG(PIL_DEBUG, "%s: names[0]=%s, names[1]=%s\n" , __FUNCTION__, names[0], names[1]); } if (dev->hmcver < 4) { if (0 == strcasecmp(names[1], FULLSYSTEMPARTITION)) { is_lpar = FALSE; snprintf(off_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -r sys -m %s -o off -n %s -c full" , dev->hmc, dev->hmc, names[0]); snprintf(on_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -r sys -m %s -o on -n %s -c full -b norm" , dev->hmc, names[0], names[0]); snprintf(reset_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -r sys -m %s -o reset -n %s -c full -b norm" , dev->hmc, names[0], names[0]); *state_cmd = 0; }else{ is_lpar = TRUE; snprintf(off_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s reset_partition" " -m %s -p %s -t hard" , dev->hmc, names[0], names[1]); snprintf(on_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -r lpar -m %s -o on -n %s" , dev->hmc, names[0], names[1]); *reset_cmd = 0; snprintf(state_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg" " -r lpar -m %s -F state -n %s" , dev->hmc, names[0], names[1]); } }else{ is_lpar = TRUE; snprintf(off_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -m %s -r lpar -o shutdown -n \"%s\" --immed" , dev->hmc, names[0], names[1]); snprintf(on_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg" " -m %s -r lpar -F \"default_profile\"" " --filter \"lpar_names=%s\"" , dev->hmc, names[0], names[1]); output = do_shell_cmd(on_cmd, &status, dev->password); if (output == NULL) { LOG(PIL_CRIT, "command %s failed", on_cmd); return (S_OOPS); } if ((pch = strchr(output, '\n')) != NULL) { *pch = 0; } snprintf(on_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -m %s -r lpar -o on -n %s -f %s" , dev->hmc, names[0], names[1], output); FREE(output); output = NULL; snprintf(reset_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s chsysstate" " -m %s -r lpar -o shutdown -n %s --immed --restart" , dev->hmc, names[0], names[1]); snprintf(state_cmd, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg" " -m %s -r lpar -F state --filter \"lpar_names=%s\"" , dev->hmc, names[0], names[1]); } g_strfreev(names); if(Debug){ LOG(PIL_DEBUG, "%s: off_cmd=%s, on_cmd=%s," "reset_cmd=%s, state_cmd=%s\n" , __FUNCTION__, off_cmd, on_cmd, reset_cmd, state_cmd); } output = do_shell_cmd(state_cmd, &status, dev->password); if (output == NULL) { LOG(PIL_CRIT, "command %s failed", on_cmd); return S_OOPS; } if ((pch = strchr(output, '\n')) != NULL) { *pch = 0; } if (strcmp(output, "Running") == 0 || strcmp(output, "Starting") == 0 || strcmp(output, "Open Firmware") == 0) { state = STATE_ON; }else if (strcmp(output, "Shutting Down") == 0 || strcmp(output, "Not Activated") == 0 || strcmp(output, "Ready") == 0) { state = STATE_OFF; }else if (strcmp(output, "Not Available") == 0 || strcmp(output, "Error") == 0) { state = STATE_INVALID; } FREE(output); output = NULL; if (state == STATE_INVALID) { LOG(PIL_CRIT, "host %s in invalid state", host); return S_OOPS; } switch (request) { case ST_POWERON: if (state == STATE_ON) { LOG(PIL_INFO, "host %s already on", host); return S_OK; } output = do_shell_cmd(on_cmd, &status, dev->password); if (0 != status) { LOG(PIL_CRIT, "command %s failed", on_cmd); return S_OOPS; } break; case ST_POWEROFF: if (state == STATE_OFF) { LOG(PIL_INFO, "host %s already off", host); return S_OK; } output = do_shell_cmd(off_cmd, &status, dev->password); if (0 != status) { LOG(PIL_CRIT, "command %s failed", off_cmd); return S_OOPS; } break; case ST_GENERIC_RESET: if (dev->hmcver < 4) { if (is_lpar) { if (state == STATE_ON) { output = do_shell_cmd(off_cmd , &status, dev->password); if (0 != status) { LOG(PIL_CRIT, "command %s " "failed", off_cmd); return S_OOPS; } } for (i = 0; i < MAX_POWERON_RETRY; i++) { char *output2; output2 = do_shell_cmd(on_cmd , &status, dev->password); if (output2 != NULL) { FREE(output2); } if (0 != status) { sleep(1); }else{ break; } } if (MAX_POWERON_RETRY == i) { LOG(PIL_CRIT, "command %s failed" , on_cmd); return S_OOPS; } }else{ output = do_shell_cmd(reset_cmd , &status, dev->password); if (0 != status) { LOG(PIL_CRIT, "command %s failed" , reset_cmd); return S_OOPS; } break; } }else{ if (state == STATE_ON) { output = do_shell_cmd(reset_cmd , &status, dev->password); }else{ output = do_shell_cmd(on_cmd , &status, dev->password); } if (0 != status) { LOG(PIL_CRIT, "command %s failed", reset_cmd); return S_OOPS; } } break; default: return S_INVAL; } if (output != NULL) { FREE(output); } LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request); return S_OK; } /* * Parse the information in the given configuration file, * and stash it away... */ static int ibmhmc_set_config(StonithPlugin * s, StonithNVpair* list) { struct pluginDevice* dev = NULL; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {NULL, NULL} }; int rc; char get_hmcver[MAX_CMD_LEN]; char firstchar; int firstnum; char* output = NULL; int status; const char *mansyspats; int len; ERRIFWRONGDEV(s,S_OOPS); if(Debug){ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__); } dev = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } if(Debug){ LOG(PIL_DEBUG, "%s: ipaddr=%s\n", __FUNCTION__ , namestocopy[0].s_value); } if (get_num_tokens(namestocopy[0].s_value) == 1) { /* name=value pairs on command line, look for managedsyspat */ mansyspats = OurImports->GetValue(list, ST_MANSYSPAT); if (mansyspats != NULL) { if (get_hmc_mansyspats(dev, mansyspats) != S_OK) { FREE(namestocopy[0].s_value); return S_OOPS; } } /* look for password */ dev->password = STRDUP(OurImports->GetValue(list, ST_PASSWD)); dev->hmc = namestocopy[0].s_value; }else{ /* -p or -F option with args "ipaddr [managedsyspat]..." */ char *pch = namestocopy[0].s_value; /* skip over ipaddr and null-terminate */ pch += strcspn(pch, WHITESPACE); *pch = EOS; /* skip over white-space up to next token */ pch++; pch += strspn(pch, WHITESPACE); if (get_hmc_mansyspats(dev, pch) != S_OK) { FREE(namestocopy[0].s_value); return S_OOPS; } dev->hmc = STRDUP(namestocopy[0].s_value); FREE(namestocopy[0].s_value); } /* check whether the HMC has ssh command enabled */ if (check_hmc_status(dev) != S_OK) { LOG(PIL_CRIT, "HMC %s does not have remote " "command execution using the ssh facility enabled", dev->hmc); return S_BADCONFIG; } /* get the HMC's version info */ snprintf(get_hmcver, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lshmc -v | grep RM", dev->hmc); if (Debug) { LOG(PIL_DEBUG, "%s: get_hmcver=%s", __FUNCTION__, get_hmcver); } output = do_shell_cmd(get_hmcver, &status, dev->password); if (Debug) { LOG(PIL_DEBUG, "%s: output=%s\n", __FUNCTION__ , output ? output : "(nil)"); } if (output == NULL) { return S_BADCONFIG; } /* parse the HMC's version info (i.e. "*RM V4R2.1" or "*RM R3V2.6") */ if ((sscanf(output, "*RM %c%1d", &firstchar, &firstnum) == 2) && ((firstchar == 'V') || (firstchar == 'R'))) { dev->hmcver = firstnum; if(Debug){ LOG(PIL_DEBUG, "%s: HMC %s version is %d" , __FUNCTION__, dev->hmc, dev->hmcver); } }else{ LOG(PIL_CRIT, "%s: unable to determine HMC %s version" , __FUNCTION__, dev->hmc); FREE(output); return S_BADCONFIG; } len = strlen(output+4) + sizeof(DEVICE) + 1; if (dev->idinfo != NULL) { FREE(dev->idinfo); dev->idinfo = NULL; } dev->idinfo = MALLOC(len * sizeof(char)); if (dev->idinfo == NULL) { LOG(PIL_CRIT, "out of memory"); FREE(output); return S_OOPS; } snprintf(dev->idinfo, len, "%s %s", DEVICE, output+4); FREE(output); if (S_OK != get_hmc_hostlist(dev)){ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s" , __FUNCTION__); return S_BADCONFIG; } return S_OK; } static const char* ibmhmc_getinfo(StonithPlugin* s, int reqtype) { struct pluginDevice* dev; const char* ret; ERRIFWRONGDEV(s,NULL); dev = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = dev->idinfo; break; case ST_DEVICENAME: ret = dev->hmc; break; case ST_DEVICEDESCR: ret = "IBM Hardware Management Console (HMC)\n" "Use for IBM i5, p5, pSeries and OpenPower systems " "managed by HMC\n" " Optional parameter name " ST_MANSYSPAT " is " "white-space delimited list of\n" "patterns used to match managed system names; if last " "character is '*',\n" "all names that begin with the pattern are matched\n" " Optional parameter name " ST_PASSWD " is password " "for " HMCROOT " if passwordless\n" "ssh access to HMC has NOT been setup (to do so, it " "is necessary to create\n" "a public/private key pair with empty passphrase - " "see \"Configure the\n" "OpenSSH client\" in the redbook for more details)"; break; case ST_DEVICEURL: ret = HMCURL; break; case ST_CONF_XML: /* XML metadata */ ret = ibmhmcXML; break; default: ret = NULL; break; } return ret; } /* * HMC Stonith destructor... */ static void ibmhmc_destroy(StonithPlugin *s) { struct pluginDevice* dev; if(Debug){ LOG(PIL_DEBUG, "%s : called\n", __FUNCTION__); } VOIDERRIFWRONGDEV(s); dev = (struct pluginDevice *)s; dev->pluginid = NOTpluginID; if (dev->hmc) { FREE(dev->hmc); dev->hmc = NULL; } if (dev->password) { FREE(dev->password); dev->password = NULL; } if (dev->idinfo) { FREE(dev->idinfo); dev->idinfo = NULL; } free_hmc_hostlist(dev); free_hmc_mansyspats(dev); FREE(dev); } static StonithPlugin * ibmhmc_new(const char *subplugin) { struct pluginDevice* dev = ST_MALLOCT(struct pluginDevice); if(Debug){ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__); } if (dev == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return(NULL); } memset(dev, 0, sizeof(*dev)); dev->pluginid = pluginid; dev->hmc = NULL; dev->password = NULL; dev->hostlist = NULL; dev->mansyspats = NULL; dev->hmcver = -1; REPLSTR(dev->idinfo, DEVICE); if (dev->idinfo == NULL) { FREE(dev); return(NULL); } dev->sp.s_ops = &ibmhmcOps; if(Debug){ LOG(PIL_DEBUG, "%s: returning successfully\n", __FUNCTION__); } return((void *)dev); } static int get_hmc_hostlist(struct pluginDevice* dev) { int i, j, status; char* output = NULL; char get_syslist[MAX_CMD_LEN]; char host[MAX_HOST_NAME_LEN]; gchar** syslist = NULL; gchar** name_mode = NULL; char get_lpar[MAX_CMD_LEN]; gchar** lparlist = NULL; char* pch; if(Debug){ LOG(PIL_DEBUG, "%s: called, dev->hmc=%s\n", __FUNCTION__ , dev->hmc); } if (dev->hmc == NULL || *dev->hmc == 0){ return S_BADCONFIG; } /* get the managed system's names of the hmc */ if (dev->hmcver < 4) { snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD " -l " HMCROOT " %s lssyscfg -r sys -F name:mode --all", dev->hmc); }else{ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD " -l " HMCROOT " %s lssyscfg -r sys -F name", dev->hmc); } if(Debug){ LOG(PIL_DEBUG, "%s: get_syslist=%s", __FUNCTION__, get_syslist); } output = do_shell_cmd(get_syslist, &status, dev->password); if (output == NULL) { return S_BADCONFIG; } syslist = g_strsplit(output, "\n", 0); FREE(output); /* for each managed system */ for (i = 0; syslist[i] != NULL && syslist[i][0] != 0; i++) { if (dev->hmcver < 4) { name_mode = g_strsplit(syslist[i], ":", 2); if(Debug){ LOG(PIL_DEBUG, "%s: name_mode0=%s, name_mode1=%s\n" , __FUNCTION__, name_mode[0], name_mode[1]); } if (dev->mansyspats != NULL && !pattern_match(dev->mansyspats, name_mode[0])) { continue; } /* if it is in fullsystempartition */ if (NULL != name_mode[1] && 0 == strncmp(name_mode[1], "0", 1)) { /* add the FullSystemPartition */ snprintf(host, MAX_HOST_NAME_LEN , "%s/FullSystemPartition", name_mode[0]); dev->hostlist = g_list_append(dev->hostlist , STRDUP(host)); }else if (NULL != name_mode[1] && 0 == strncmp(name_mode[1], "255", 3)){ /* get its lpars */ snprintf(get_lpar, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg -m %s -r lpar -F name --all" , dev->hmc, name_mode[0]); if(Debug){ LOG(PIL_DEBUG, "%s: get_lpar=%s\n" , __FUNCTION__, get_lpar); } output = do_shell_cmd(get_lpar , &status, dev->password); if (output == NULL) { g_strfreev(name_mode); g_strfreev(syslist); return S_BADCONFIG; } lparlist = g_strsplit(output, "\n", 0); FREE(output); /* for each lpar */ for (j = 0 ; NULL != lparlist[j] && 0 != lparlist[j][0] ; j++) { /* skip the full system partition */ if (0 == strncmp(lparlist[j] , FULLSYSTEMPARTITION , strlen(FULLSYSTEMPARTITION))) { continue; } /* add the lpar */ snprintf(host, MAX_HOST_NAME_LEN , "%s/%s", name_mode[0] , lparlist[j]); dev->hostlist = g_list_append(dev->hostlist , STRDUP(host)); } g_strfreev(lparlist); } g_strfreev(name_mode); }else{ if (dev->mansyspats != NULL && !pattern_match(dev->mansyspats, syslist[i])) { continue; } /* get its state */ snprintf(get_lpar, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg -m %s -r sys -F state" , dev->hmc, syslist[i]); if(Debug){ LOG(PIL_DEBUG, "%s: get_lpar=%s\n" , __FUNCTION__, get_lpar); } output = do_shell_cmd(get_lpar, &status, dev->password); if (output == NULL) { g_strfreev(syslist); return S_BADCONFIG; } if ((pch = strchr(output, '\n')) != NULL) { *pch = 0; } if (!strcmp(output, "No Connection")){ FREE(output); continue; } FREE(output); /* get its lpars */ snprintf(get_lpar, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lssyscfg -m %s -r lpar -F name" , dev->hmc, syslist[i]); if(Debug){ LOG(PIL_DEBUG, "%s: get_lpar=%s\n" , __FUNCTION__, get_lpar); } output = do_shell_cmd(get_lpar, &status, dev->password); if (output == NULL) { g_strfreev(syslist); return S_BADCONFIG; } lparlist = g_strsplit(output, "\n", 0); FREE(output); /* for each lpar */ for (j = 0 ; NULL != lparlist[j] && 0 != lparlist[j][0] ; j++) { /* add the lpar */ snprintf(host, MAX_HOST_NAME_LEN , "%s/%s", syslist[i],lparlist[j]); dev->hostlist = g_list_append(dev->hostlist , STRDUP(host)); } g_strfreev(lparlist); } } g_strfreev(syslist); return S_OK; } static void free_hmc_hostlist(struct pluginDevice* dev) { if (dev->hostlist) { GList* node; while (NULL != (node=g_list_first(dev->hostlist))) { dev->hostlist = g_list_remove_link(dev->hostlist, node); FREE(node->data); g_list_free(node); } dev->hostlist = NULL; } } static int get_hmc_mansyspats(struct pluginDevice * dev, const char *mansyspats) { char *patscopy; int numpats; int i; char *tmp; if(Debug){ LOG(PIL_DEBUG, "%s: called, mansyspats=%s\n" , __FUNCTION__, mansyspats); } patscopy = STRDUP(mansyspats); if (patscopy == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return S_OOPS; } numpats = get_num_tokens(patscopy); if (numpats > 0) { dev->mansyspats = MALLOC((numpats+1)*sizeof(char *)); if (dev->mansyspats == NULL) { LOG(PIL_CRIT, "%s: out of memory" , __FUNCTION__); FREE(patscopy); return S_OOPS; } memset(dev->mansyspats, 0, (numpats+1)*sizeof(char *)); /* White-space split the output here */ i = 0; tmp = strtok(patscopy, WHITESPACE); while (tmp != NULL) { dev->mansyspats[i] = STRDUP(tmp); if (dev->mansyspats[i] == NULL) { LOG(PIL_CRIT, "%s: out of memory" , __FUNCTION__); free_hmc_mansyspats(dev); dev->mansyspats = NULL; FREE(patscopy); return S_OOPS; } if(Debug){ LOG(PIL_DEBUG, "%s: adding pattern %s\n" , __FUNCTION__, dev->mansyspats[i]); } /* no patterns necessary if all specified */ if (strcmp(dev->mansyspats[i], "*") == 0) { stonith_free_hostlist(dev->mansyspats); dev->mansyspats = NULL; break; } i++; tmp = strtok(NULL, WHITESPACE); } } FREE(patscopy); return S_OK; } static void free_hmc_mansyspats(struct pluginDevice* dev) { if (dev->mansyspats) { stonith_free_hostlist(dev->mansyspats); dev->mansyspats = NULL; } } static char* do_shell_cmd(const char* cmd, int* status, const char* password) { const int BUFF_LEN=4096; int read_len = 0; char buff[BUFF_LEN]; char cmd_password[MAX_CMD_LEN]; char* data = NULL; GString* g_str_tmp = NULL; FILE* file; if (NULL == password) { file = popen(cmd, "r"); } else { snprintf(cmd_password, MAX_CMD_LEN ,"umask 077;" "if [ ! -d " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc ];" "then mkdir " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc 2>/dev/null;" "fi;" "export ibmhmc_tmp=`mktemp -p " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc/`;" "echo \"echo '%s'\">$ibmhmc_tmp;" "chmod +x $ibmhmc_tmp;" "unset SSH_AGENT_SOCK SSH_AGENT_PID;" "SSH_ASKPASS=$ibmhmc_tmp DISPLAY=ibmhmc_foo setsid %s;" "rm $ibmhmc_tmp -f;" "unset ibmhmc_tmp" ,password, cmd); file = popen(cmd_password, "r"); } if (NULL == file) { return NULL; } g_str_tmp = g_string_new(""); while(!feof(file)) { memset(buff, 0, BUFF_LEN); read_len = fread(buff, 1, BUFF_LEN, file); if (0 < read_len) { g_string_append(g_str_tmp, buff); }else{ sleep(1); } } data = (char*)MALLOC(g_str_tmp->len+1); if (data != NULL) { data[0] = data[g_str_tmp->len] = 0; strncpy(data, g_str_tmp->str, g_str_tmp->len); } g_string_free(g_str_tmp, TRUE); *status = pclose(file); return data; } static int check_hmc_status(struct pluginDevice* dev) { int status; char check_status[MAX_CMD_LEN]; char* output = NULL; int rc = S_OK; if(Debug){ LOG(PIL_DEBUG, "%s: called, hmc=%s\n", __FUNCTION__, dev->hmc); } snprintf(check_status, MAX_CMD_LEN , SSH_CMD " -l " HMCROOT " %s lshmc -r -F ssh", dev->hmc); if(Debug){ LOG(PIL_DEBUG, "%s: check_status %s\n", __FUNCTION__ , check_status); } output = do_shell_cmd(check_status, &status, dev->password); if (Debug) { LOG(PIL_DEBUG, "%s: status=%d, output=%s\n", __FUNCTION__ , status, output ? output : "(nil)"); } if (NULL == output || strncmp(output, "enable", 6) != 0) { rc = S_BADCONFIG; } if (NULL != output) { FREE(output); } return rc; } static int get_num_tokens(char *str) { int namecount = 0; while (*str != EOS) { str += strspn(str, WHITESPACE); if (*str == EOS) break; str += strcspn(str, WHITESPACE); namecount++; } return namecount; } static gboolean pattern_match(char **patterns, char *string) { char **pattern; if(Debug){ LOG(PIL_DEBUG, "%s: called, string=%s\n", __FUNCTION__, string); } for (pattern = patterns; *pattern; pattern++) { int patlen = strlen(*pattern); if (pattern[0][patlen-1] == '*') { /* prefix match */ if (strncmp(string, *pattern, patlen-1) == 0) { return TRUE; } }else{ /* exact match */ if (strcmp(string, *pattern) == 0) { return TRUE; } } } return FALSE; } /* static char* do_shell_cmd_fake(const char* cmd, int* status) { printf("%s()\n", __FUNCTION__); printf("cmd:%s\n", cmd); *status=0; return NULL; } */ Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ipmi_os_handler.c0000644000000000000000000001335012120057602030502 0ustar00usergroup00000000000000/* * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package. * * Copyright Intel Corp. * Yixiong.Zou@intel.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include extern selector_t *os_sel; #if 0 static void check_no_locks(os_handler_t *handler); #define CHECK_NO_LOCKS(handler) check_no_locks(handler) #else #define CHECK_NO_LOCKS(handler) do {} while(0) #endif struct os_hnd_fd_id_s { int fd; void *cb_data; os_data_ready_t data_ready; os_handler_t *handler; }; static void fd_handler(int fd, void *data) { os_hnd_fd_id_t *fd_data = (os_hnd_fd_id_t *) data; CHECK_NO_LOCKS(fd_data->handler); fd_data->data_ready(fd, fd_data->cb_data, fd_data); CHECK_NO_LOCKS(fd_data->handler); } static int add_fd(os_handler_t *handler, int fd, os_data_ready_t data_ready, void *cb_data, os_hnd_fd_id_t **id) { os_hnd_fd_id_t *fd_data; fd_data = ipmi_mem_alloc(sizeof(*fd_data)); if (!fd_data) return ENOMEM; fd_data->fd = fd; fd_data->cb_data = cb_data; fd_data->data_ready = data_ready; fd_data->handler = handler; sel_set_fd_handlers(os_sel, fd, fd_data, fd_handler, NULL, NULL, NULL); sel_set_fd_read_handler(os_sel, fd, SEL_FD_HANDLER_ENABLED); sel_set_fd_write_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED); sel_set_fd_except_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED); *id = fd_data; return 0; } static int remove_fd(os_handler_t *handler, os_hnd_fd_id_t *fd_data) { sel_clear_fd_handlers(os_sel, fd_data->fd); sel_set_fd_read_handler(os_sel, fd_data->fd, SEL_FD_HANDLER_DISABLED); ipmi_mem_free(fd_data); return 0; } struct os_hnd_timer_id_s { void *cb_data; os_timed_out_t timed_out; sel_timer_t *timer; int running; os_handler_t *handler; }; static void timer_handler(selector_t *sel, sel_timer_t *timer, void *data) { os_hnd_timer_id_t *timer_data = (os_hnd_timer_id_t *) data; void *cb_data; os_timed_out_t timed_out; CHECK_NO_LOCKS(timer_data->handler); timed_out = timer_data->timed_out; cb_data = timer_data->cb_data; timer_data->running = 0; timed_out(cb_data, timer_data); CHECK_NO_LOCKS(timer_data->handler); } static int start_timer(os_handler_t *handler, os_hnd_timer_id_t *id, struct timeval *timeout, os_timed_out_t timed_out, void *cb_data) { struct timeval now; if (id->running) return EBUSY; id->running = 1; id->cb_data = cb_data; id->timed_out = timed_out; gettimeofday(&now, NULL); now.tv_sec += timeout->tv_sec; now.tv_usec += timeout->tv_usec; while (now.tv_usec >= 1000000) { now.tv_usec -= 1000000; now.tv_sec += 1; } return sel_start_timer(id->timer, &now); } static int stop_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data) { return sel_stop_timer(timer_data->timer); } static int alloc_timer(os_handler_t *handler, os_hnd_timer_id_t **id) { os_hnd_timer_id_t *timer_data; int rv; timer_data = ipmi_mem_alloc(sizeof(*timer_data)); if (!timer_data) return ENOMEM; timer_data->running = 0; timer_data->timed_out = NULL; timer_data->handler = handler; rv = sel_alloc_timer(os_sel, timer_handler, timer_data, &(timer_data->timer)); if (rv) { ipmi_mem_free(timer_data); return rv; } *id = timer_data; return 0; } static int free_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data) { sel_free_timer(timer_data->timer); ipmi_mem_free(timer_data); return 0; } static int get_random(os_handler_t *handler, void *data, unsigned int len) { int fd = open("/dev/urandom", O_RDONLY); int rv; if (fd == -1) return errno; rv = read(fd, data, len); close(fd); return rv; } static void sui_log(os_handler_t *handler, enum ipmi_log_type_e log_type, char *format, ...) { return; } static void sui_vlog(os_handler_t *handler, enum ipmi_log_type_e log_type, char *format, va_list ap) { return; } os_handler_t ipmi_os_cb_handlers = { .add_fd_to_wait_for = add_fd, .remove_fd_to_wait_for = remove_fd, .start_timer = start_timer, .stop_timer = stop_timer, .alloc_timer = alloc_timer, .free_timer = free_timer, .create_lock = NULL, .destroy_lock = NULL, .is_locked = NULL, .lock = NULL, .unlock = NULL, .create_rwlock = NULL, .destroy_rwlock = NULL, .read_lock = NULL, .write_lock = NULL, .read_unlock = NULL, .write_unlock = NULL, .is_readlocked = NULL, .is_writelocked = NULL, .get_random = get_random, .log = sui_log, .vlog = sui_vlog }; Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ipmilan.c0000644000000000000000000003345612120057602027010 0ustar00usergroup00000000000000/* * Stonith module for ipmi lan Stonith device * * Copyright (c) 2003 Intel Corp. * Yixiong Zou * * Mangled by Sun Jiang Dong , IBM, 2005. * And passed the compiling with OpenIPMI-1.4.8. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * See README.ipmi for information regarding this plugin. * */ #define DEVICE "IPMI Over LAN" #include "stonith_plugin_common.h" #define PIL_PLUGIN ipmilan #define PIL_PLUGIN_S "ipmilan" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include #include #include "ipmilan.h" static StonithPlugin * ipmilan_new(const char *); static void ipmilan_destroy(StonithPlugin *); static const char * const * ipmilan_get_confignames(StonithPlugin *); static int ipmilan_set_config(StonithPlugin *, StonithNVpair *); static const char * ipmilan_getinfo(StonithPlugin * s, int InfoType); static int ipmilan_status(StonithPlugin * ); static int ipmilan_reset_req(StonithPlugin * s, int request, const char * host); static char ** ipmilan_hostlist(StonithPlugin *); static struct stonith_ops ipmilanOps ={ ipmilan_new, /* Create new STONITH object */ ipmilan_destroy, /* Destroy STONITH object */ ipmilan_getinfo, /* Return STONITH info string */ ipmilan_get_confignames,/* Get configuration parameter names */ ipmilan_set_config, /* Set configuration */ ipmilan_status, /* Return STONITH device status */ ipmilan_reset_req, /* Request a reset */ ipmilan_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug); const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &ipmilanOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * ipmilan STONITH device. * * ipmilanHostInfo is a double linked list. Where the prev of the head always * points to the tail. This is a little wierd. But it saves me from looping * around to find the tail when destroying the list. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; int hostcount; struct ipmilanHostInfo * hostlist; }; static const char * pluginid = "IPMI-LANDevice-Stonith"; static const char * NOTpluginid = "IPMI-LAN device has been destroyed"; #define ST_HOSTNAME "hostname" #define ST_PORT "port" #define ST_AUTH "auth" #define ST_PRIV "priv" #define ST_RESET_METHOD "reset_method" #include "stonith_config_xml.h" #define XML_HOSTNAME_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_HOSTNAME \ XML_PARM_SHORTDESC_END #define XML_HOSTNAME_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The hostname of the STONITH device" \ XML_PARM_LONGDESC_END #define XML_HOSTNAME_PARM \ XML_PARAMETER_BEGIN(ST_HOSTNAME, "string", "1") \ XML_HOSTNAME_SHORTDESC \ XML_HOSTNAME_LONGDESC \ XML_PARAMETER_END #define XML_PORT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_PORT \ XML_PARM_SHORTDESC_END #define XML_PORT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The port number to where the IPMI message is sent" \ XML_PARM_LONGDESC_END #define XML_PORT_PARM \ XML_PARAMETER_BEGIN(ST_PORT, "string", "1") \ XML_PORT_SHORTDESC \ XML_PORT_LONGDESC \ XML_PARAMETER_END #define XML_AUTH_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_AUTH \ XML_PARM_SHORTDESC_END #define XML_AUTH_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The authorization type of the IPMI session (\"none\", \"straight\", \"md2\", or \"md5\")" \ XML_PARM_LONGDESC_END #define XML_AUTH_PARM \ XML_PARAMETER_BEGIN(ST_AUTH, "string", "1") \ XML_AUTH_SHORTDESC \ XML_AUTH_LONGDESC \ XML_PARAMETER_END #define XML_PRIV_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_PRIV \ XML_PARM_SHORTDESC_END #define XML_PRIV_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The privilege level of the user (\"operator\" or \"admin\")" \ XML_PARM_LONGDESC_END #define XML_PRIV_PARM \ XML_PARAMETER_BEGIN(ST_PRIV, "string", "1") \ XML_PRIV_SHORTDESC \ XML_PRIV_LONGDESC \ XML_PARAMETER_END #define XML_RESET_METHOD_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_RESET_METHOD \ XML_PARM_SHORTDESC_END #define XML_RESET_METHOD_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "How to reset the host (\"power_cycle\" or \"hard_reset\")" \ XML_PARM_LONGDESC_END #define XML_RESET_METHOD_PARM \ XML_PARAMETER_BEGIN(ST_RESET_METHOD, "string", "0") \ XML_RESET_METHOD_SHORTDESC \ XML_RESET_METHOD_LONGDESC \ XML_PARAMETER_END static const char *ipmilanXML = XML_PARAMETERS_BEGIN XML_HOSTNAME_PARM XML_IPADDR_PARM XML_PORT_PARM XML_AUTH_PARM XML_PRIV_PARM XML_LOGIN_PARM XML_PASSWD_PARM XML_PARAMETERS_END; /* * Check the status of the IPMI Lan STONITH device. * * NOTE: not sure what we should do here since each host is configured * seperately. * * Two options: * 1) always return S_OK. * 2) using IPMI ping to confirm the status for every host that's * configured. * * For now I choose the option 1 hoping that I can get by. Maybe we should * change it to option 2 later. */ static int ipmilan_status(StonithPlugin *s) { struct pluginDevice * nd; struct ipmilanHostInfo * node; int ret, rv; int i; ERRIFWRONGDEV(s,S_OOPS); ret = S_OK; nd = (struct pluginDevice *)s; for( i=0, node = nd->hostlist; i < nd->hostcount; i++, node = node->next ) { rv = do_ipmi_cmd(node, ST_IPMI_STATUS); if (rv) { LOG(PIL_INFO, "Host %s ipmilan status failure." , node->hostname); ret = S_ACCESS; } else { LOG(PIL_INFO, "Host %s ipmilan status OK." , node->hostname); } } return ret; } /* * This function returns the list of hosts that's configured. * * The detailed configuration is disabled because the STONITH command can be * run by anyone so there is a security risk if that to be exposed. */ static char * get_config_string(struct pluginDevice * nd, int index) { struct ipmilanHostInfo * host; int i; if (index >= nd->hostcount || index < 0) { return (NULL); } host = nd->hostlist; for (i = 0; i < index; i++) { host = host->next; } return STRDUP(host->hostname); } /* * Return the list of hosts configured for this ipmilan device * */ static char ** ipmilan_hostlist(StonithPlugin *s) { int numnames = 0; char ** ret = NULL; struct pluginDevice* nd; int j; ERRIFWRONGDEV(s,NULL); nd = (struct pluginDevice*) s; if (nd->hostcount < 0) { LOG(PIL_CRIT , "unconfigured stonith object in ipmi_hostlist"); return(NULL); } numnames = nd->hostcount; ret = (char **)MALLOC((numnames + 1)*sizeof(char*)); if (ret == NULL) { LOG(PIL_CRIT, "out of memory"); return (ret); } memset(ret, 0, (numnames + 1)*sizeof(char*)); for (j = 0; j < numnames; ++j) { ret[j] = get_config_string(nd, j); if (!ret[j]) { stonith_free_hostlist(ret); ret = NULL; break; } g_strdown(ret[j]); } return(ret); } /* * Parse the config information, and stash it away... * * The buffer for each string is MAX_IPMI_STRING_LEN bytes long. * Right now it is set to 64. Hope this is enough. * */ #define MAX_IPMI_STRING_LEN 64 /* * Reset the given host on this StonithPlugin device. */ static int ipmilan_reset_req(StonithPlugin * s, int request, const char * host) { int rc = 0; struct pluginDevice * nd; struct ipmilanHostInfo * node; int i; ERRIFWRONGDEV(s,S_OOPS); nd = (struct pluginDevice *)s; for( i=0, node = nd->hostlist; i < nd->hostcount; i++, node = node->next ) { if (strcasecmp(node->hostname, host) == 0) { break; } } if (i >= nd->hostcount) { LOG(PIL_CRIT, "Host %s is not configured in this STONITH " " module. Please check your configuration file.", host); return (S_OOPS); } rc = do_ipmi_cmd(node, request); if (!rc) { LOG(PIL_INFO, "Host %s ipmilan-reset.", host); } else { LOG(PIL_INFO, "Host %s ipmilan-reset error. Error = %d." , host, rc); } return rc; } /* * Get configuration parameter names */ static const char * const * ipmilan_get_confignames(StonithPlugin * s) { static const char * ret[] = { ST_HOSTNAME, ST_IPADDR, ST_PORT, ST_AUTH, ST_PRIV, ST_LOGIN, ST_PASSWD, ST_RESET_METHOD, NULL}; return ret; } /* * Set the configuration parameters */ static int ipmilan_set_config(StonithPlugin* s, StonithNVpair * list) { struct pluginDevice* nd; int rc; struct ipmilanHostInfo * tmp; const char *reset_opt; StonithNamesToGet namestocopy [] = { {ST_HOSTNAME, NULL} , {ST_IPADDR, NULL} , {ST_PORT, NULL} , {ST_AUTH, NULL} , {ST_PRIV, NULL} , {ST_LOGIN, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s,S_OOPS); nd = (struct pluginDevice *)s; ERRIFWRONGDEV(s, S_OOPS); if (nd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } tmp = ST_MALLOCT(struct ipmilanHostInfo); tmp->hostname = namestocopy[0].s_value; tmp->ipaddr = namestocopy[1].s_value; tmp->portnumber = atoi(namestocopy[2].s_value); FREE(namestocopy[2].s_value); if (namestocopy[3].s_value == NULL) { LOG(PIL_CRIT, "ipmilan auth type is NULL. See " "README.ipmilan for allowed values"); return S_OOPS; } else if (strcmp(namestocopy[3].s_value, "none") == 0) { tmp->authtype = 0; } else if (strcmp(namestocopy[3].s_value, "md2") == 0) { tmp->authtype = 1; } else if (strcmp(namestocopy[3].s_value, "md5") == 0) { tmp->authtype = 2; } else if (strcmp(namestocopy[3].s_value, "key") == 0 || strcmp(namestocopy[3].s_value, "password") == 0 || strcmp(namestocopy[3].s_value, "straight") == 0) { tmp->authtype = 4; } else { LOG(PIL_CRIT, "ipmilan auth type '%s' invalid. See " "README.ipmilan for allowed values", namestocopy[3].s_value); return S_OOPS; } FREE(namestocopy[3].s_value); if (namestocopy[4].s_value == NULL) { LOG(PIL_CRIT, "ipmilan priv value is NULL. See " "README.ipmilan for allowed values"); return S_OOPS; } else if (strcmp(namestocopy[4].s_value, "operator") == 0) { tmp->privilege = 3; } else if (strcmp(namestocopy[4].s_value, "admin") == 0) { tmp->privilege = 4; } else { LOG(PIL_CRIT, "ipmilan priv value '%s' invalid. See " "README.ipmilan for allowed values", namestocopy[4].s_value); return(S_OOPS); } FREE(namestocopy[4].s_value); tmp->username = namestocopy[5].s_value; tmp->password = namestocopy[6].s_value; reset_opt = OurImports->GetValue(list, ST_RESET_METHOD); if (!reset_opt || !strcmp(reset_opt, "power_cycle")) { tmp->reset_method = 0; } else if (!strcmp(reset_opt, "hard_reset")) { tmp->reset_method = 1; } else { LOG(PIL_CRIT, "ipmilan reset_method '%s' invalid", reset_opt); return S_OOPS; } if (nd->hostlist == NULL ) { nd->hostlist = tmp; nd->hostlist->prev = tmp; nd->hostlist->next = tmp; } else { tmp->prev = nd->hostlist->prev; tmp->next = nd->hostlist; nd->hostlist->prev->next = tmp; nd->hostlist->prev = tmp; } nd->hostcount++; return(S_OK); } static const char * ipmilan_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice * nd; const char * ret; ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ nd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = nd->idinfo; break; case ST_DEVICENAME: ret = nd->hostlist ? nd->hostlist->hostname : NULL; break; case ST_DEVICEDESCR: ret = "IPMI LAN STONITH device\n"; break; case ST_DEVICEURL: ret = "http://www.intel.com/design/servers/ipmi/"; break; case ST_CONF_XML: /* XML metadata */ ret = ipmilanXML; break; default: ret = NULL; break; } return ret; } /* * ipmilan StonithPlugin destructor... * * The hostlist is a link list. So have to iterate through. */ static void ipmilan_destroy(StonithPlugin *s) { struct pluginDevice* nd; struct ipmilanHostInfo * host; int i; VOIDERRIFWRONGDEV(s); nd = (struct pluginDevice *)s; nd->pluginid = NOTpluginid; if (nd->hostlist) { host = nd->hostlist->prev; for (i = 0; i < nd->hostcount; i++) { struct ipmilanHostInfo * host_prev = host->prev; FREE(host->hostname); FREE(host->ipaddr); FREE(host->username); FREE(host->password); FREE(host); host = host_prev; } } nd->hostcount = -1; FREE(nd); ipmi_leave(); } /* Create a new ipmilan StonithPlugin device. Too bad this function can't be static */ static StonithPlugin * ipmilan_new(const char *subplugin) { struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice); if (nd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(nd, 0, sizeof(*nd)); nd->pluginid = pluginid; nd->hostlist = NULL; nd->hostcount = 0; nd->idinfo = DEVICE; nd->sp.s_ops = &ipmilanOps; return(&(nd->sp)); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ipmilan.h0000644000000000000000000000234112120057602027002 0ustar00usergroup00000000000000/* * Stonith module for ipmi lan Stonith device * * Copyright (c) 2003 Intel Corp. * Yixiong Zou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define ST_IPMI_STATUS 4 #include struct ipmilanHostInfo { char * hostname; char * ipaddr; int portnumber; int authtype; int privilege; char * username; char * password; int reset_method; struct ipmilanHostInfo * prev; struct ipmilanHostInfo * next; }; int do_ipmi_cmd(struct ipmilanHostInfo * host, int request); void ipmi_leave(void); Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ipmilan_command.c0000644000000000000000000002233112120057602030474 0ustar00usergroup00000000000000/* * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package. * * Copyright Intel Corp. * Yixiong.Zou@intel.com * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* malloc() */ #include /* getopt() */ #include /* strerror() */ #include /* gethostbyname() */ #include #include #include #include #include #include #include #include #include #include #include "ipmilan.h" #include #include #include extern const PILPluginImports* PluginImports; /* #define DUMP_MSG 0 */ #define OPERATION_TIME_OUT 10 os_handler_t *os_hnd=NULL; selector_t *os_sel; static ipmi_con_t *con; extern os_handler_t ipmi_os_cb_handlers; static int reset_method; static int request_done = 0; static int op_done = 0; typedef enum ipmi_status { /* IPMI_CONNECTION_FAILURE, IPMI_SEND_FAILURE, IPMI_BAD_REQUEST, IPMI_REQUEST_FAILED, IPMI_TIME_OUT, */ IPMI_RUNNING = 99, } ipmi_status_t; static ipmi_status_t gstatus; typedef enum chassis_control_request { POWER_DOWN = 0X00, POWER_UP = 0X01, POWER_CYCLE = 0X02, HARD_RESET = 0X03, PULSE_DIAGNOSTIC_INTERRUPT = 0X04, SOFT_SHUTDOWN = 0X05 } chassis_control_request_t; void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type); int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi); void send_ipmi_cmd(ipmi_con_t *con, int request); void timed_out(selector_t *sel, sel_timer_t *timer, void *data); void timed_out(selector_t *sel, sel_timer_t *timer, void *data) { PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n"); gstatus = S_TIMEOUT; } void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type) { ipmi_system_interface_addr_t *smi_addr = NULL; int i; ipmi_ipmb_addr_t *ipmb_addr = NULL; if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { smi_addr = (struct ipmi_system_interface_addr *) addr; fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ", addr->channel, msg->netfn, smi_addr->lun, msg->cmd); } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { ipmb_addr = (struct ipmi_ipmb_addr *) addr; fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ", addr->channel, msg->netfn, ipmb_addr->lun, msg->cmd); } for (i = 0; i < msg->data_len; i++) { if (((i%16) == 0) && (i != 0)) { printf("\n "); } fprintf(stderr, "%2.2x ", msg->data[i]); } fprintf(stderr, "\n"); } /* * This function gets called after the response comes back * from the IPMI device. * * Some IPMI device does not return success, 0x00, to the * remote node when the power-reset was issued. * * The host who sent the ipmi cmd might get a 0xc3, * a timeout instead. This creates problems for * STONITH operation, where status is critical. :( * * Right now I am only checking 0xc3 as the return. * If your IPMI device returns some wired code after * reset, you might want to add it in this code block. * */ int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi) { int rv; long request; /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/ request = (long) rspi->data1; op_done = 1; if( !rspi || !(rspi->msg.data) ) { PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n"); gstatus = S_RESETFAIL; return IPMI_MSG_ITEM_NOT_USED; } rv = rspi->msg.data[0]; /* some IPMI device might not issue 0x00, success, for reset command. instead, a 0xc3, timeout, is returned. */ if (rv == 0x00) { gstatus = S_OK; } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) { PILCallLog(PluginImports->log,PIL_WARN , "IPMI reset request failed: %x, but we assume that it succeeded\n", rv); gstatus = S_OK; } else { PILCallLog(PluginImports->log,PIL_INFO , "IPMI request %ld failed: %x\n", request, rv); gstatus = S_RESETFAIL; } return IPMI_MSG_ITEM_NOT_USED; } void send_ipmi_cmd(ipmi_con_t *con, int request) { ipmi_addr_t addr; unsigned int addr_len; ipmi_msg_t msg; struct ipmi_system_interface_addr *si; int rv; ipmi_msgi_t *rspi; /* chassis control command request is only 1 byte long */ unsigned char cc_data = POWER_CYCLE; si = (void *) &addr; si->lun = 0x00; si->channel = IPMI_BMC_CHANNEL; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr_len = sizeof(*si); msg.netfn = IPMI_CHASSIS_NETFN; msg.cmd = IPMI_CHASSIS_CONTROL_CMD; msg.data = &cc_data; msg.data_len = 1; switch (request) { case ST_POWERON: cc_data = POWER_UP; break; case ST_POWEROFF: cc_data = POWER_DOWN; break; case ST_GENERIC_RESET: cc_data = (reset_method ? POWER_CYCLE : HARD_RESET); break; case ST_IPMI_STATUS: msg.netfn = IPMI_APP_NETFN; msg.cmd = IPMI_GET_DEVICE_ID_CMD; msg.data_len = 0; break; default: gstatus = S_INVAL; return; } gstatus = S_ACCESS; rspi = calloc(1, sizeof(ipmi_msgi_t)); if (NULL == rspi) { PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n"); } else { rspi->data1 = (void *) (long) request; rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi); if (rv == -1) { PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv); } else { request_done = 1; } } return; } static void con_changed_handler(ipmi_con_t *ipmi, int err, unsigned int port_num, int still_connected, void *cb_data) { int * request; if (err) { PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err); return; } if( !request_done ) { request = (int *) cb_data; send_ipmi_cmd(ipmi, *request); } } static int setup_ipmi_conn(struct ipmilanHostInfo * host, int *request) { int rv; struct hostent *ent; struct in_addr lan_addr[2]; int lan_port[2]; int num_addr = 1; int authtype = 0; int privilege = 0; char username[17]; char password[17]; /*DEBUG_MSG_ENABLE();*/ os_hnd = ipmi_posix_get_os_handler(); if (!os_hnd) { PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler"); return 1; } rv = sel_alloc_selector(os_hnd, &os_sel); if (rv) { PILCallLog(PluginImports->log,PIL_CRIT, "Could not allocate selector\n"); return rv; } ipmi_posix_os_handler_set_sel(os_hnd, os_sel); rv = ipmi_init(os_hnd); if (rv) { PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv); return rv; } ent = gethostbyname(host->ipaddr); if (!ent) { PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno)); return 1; } memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length); lan_port[0] = host->portnumber; lan_port[1] = 0; authtype = host->authtype; privilege = host->privilege; memcpy(username, host->username, sizeof(username)); memcpy(password, host->password, sizeof(password)); reset_method = host->reset_method; rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr, authtype, privilege, username, strlen(username), password, strlen(password), os_hnd, os_sel, &con); if (rv) { PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv)); return S_ACCESS; } #if OPENIPMI_VERSION_MAJOR < 2 con->set_con_change_handler(con, con_changed_handler, request); #else con->add_con_change_handler(con, con_changed_handler, request); #endif gstatus = IPMI_RUNNING; rv = con->start_con(con); if (rv) { PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv); gstatus = S_BADCONFIG; return rv; } return S_OK; } void ipmi_leave() { if( con && con->close_connection ) { con->close_connection(con); con = NULL; } if( os_sel ) { sel_free_selector(os_sel); os_sel = NULL; } } int do_ipmi_cmd(struct ipmilanHostInfo * host, int request) { int rv; sel_timer_t * timer; struct timeval timeout; request_done = 0; op_done = 0; if( !os_hnd ) { rv = setup_ipmi_conn(host, &request); if( rv ) { return rv; } } else { send_ipmi_cmd(con, request); } gettimeofday(&timeout, NULL); timeout.tv_sec += OPERATION_TIME_OUT; timeout.tv_usec += 0; sel_alloc_timer(os_sel, timed_out, NULL, &timer); sel_start_timer(timer, &timeout); while (!op_done) { rv = sel_select(os_sel, NULL, 0, NULL, NULL); if (rv == -1) { break; } } sel_free_timer(timer); return gstatus; } #if OPENIPMI_VERSION_MAJOR < 2 void posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap) { } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ipmilan_test.c0000644000000000000000000000336112120057602030037 0ustar00usergroup00000000000000/* * Stonith module for ipmi lan Stonith device * * Copyright (c) 2003 Intel Corp. * Yixiong Zou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * A quick test program to verify that IPMI host is setup correctly. * * You will need to modify the values in user, pass, ip, and port. */ #include #include #include "ipmilan.h" #include int main(int argc, char * argv[]) { struct ipmilanHostInfo host; int request = 2; int rv; char user[] = "joe"; char pass[] = "blow"; char ip[] = "192.168.1.7"; host.hostname = NULL; host.portnumber = 999; host.authtype = IPMI_AUTHTYPE_NONE; host.privilege = IPMI_PRIVILEGE_ADMIN; host.ipaddr = ip; memcpy(host.username, user, sizeof(user)); memcpy(host.password, pass, sizeof(pass)); /* memset(host.username, 0, sizeof(host.username)); memset(host.password, 0, sizeof(host.password)); */ rv = do_ipmi_cmd(&host, request); if (rv) printf("rv = %d, operation failed. \n", rv); else printf("operation succeeded. \n"); return rv; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/meatware.c0000644000000000000000000002060412120057602027153 0ustar00usergroup00000000000000/* * Stonith module for Human Operator Stonith device * * Copyright (c) 2001 Gregor Binder * * This module is largely based on the "NULL Stonith device", written * by Alan Robertson , using code by David C. Teigland * originally appeared in the GFS stomith * meatware agent. * * Mangled by Zhaokai , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "Meatware STONITH device" #include "stonith_plugin_common.h" #define PIL_PLUGIN meatware #define PIL_PLUGIN_S "meatware" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * meatware_new(const char *); static void meatware_destroy(StonithPlugin *); static int meatware_set_config(StonithPlugin *, StonithNVpair *); static const char * const * meatware_get_confignames(StonithPlugin *); static const char * meatware_getinfo(StonithPlugin * s, int InfoType); static int meatware_status(StonithPlugin * ); static int meatware_reset_req(StonithPlugin * s, int request, const char * host); static char ** meatware_hostlist(StonithPlugin *); static struct stonith_ops meatwareOps ={ meatware_new, /* Create new STONITH object */ meatware_destroy, /* Destroy STONITH object */ meatware_getinfo, /* Return STONITH info string */ meatware_get_confignames,/* Return STONITH info string */ meatware_set_config, /* Get configuration from NVpairs */ meatware_status, /* Return STONITH device status */ meatware_reset_req, /* Request a reset */ meatware_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &meatwareOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * Meatware STONITH device. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char ** hostlist; int hostcount; }; static const char * pluginid = "MeatwareDevice-Stonith"; static const char * NOTpluginID = "Meatware device has been destroyed"; #include "stonith_config_xml.h" static const char *meatwareXML = XML_PARAMETERS_BEGIN XML_HOSTLIST_PARM XML_PARAMETERS_END; static int meatware_status(StonithPlugin *s) { ERRIFWRONGDEV(s,S_OOPS); return S_OK; } /* * Return the list of hosts configured for this Meat device */ static char ** meatware_hostlist(StonithPlugin *s) { struct pluginDevice* nd; ERRIFWRONGDEV(s,NULL); nd = (struct pluginDevice*) s; if (nd->hostcount < 0) { LOG(PIL_CRIT , "unconfigured stonith object in Meatware_list_hosts"); return(NULL); } return OurImports->CopyHostList((const char * const *)nd->hostlist); } /* * Parse the config information, and stash it away... */ static int Meat_parse_config_info(struct pluginDevice* nd, const char * info) { LOG(PIL_INFO , "parse config info info=%s",info); if (nd->hostcount >= 0) { return(S_OOPS); } nd->hostlist = OurImports->StringToHostList(info); if (nd->hostlist == NULL) { LOG(PIL_CRIT,"StringToHostList() failed"); return S_OOPS; } for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) { g_strdown(nd->hostlist[nd->hostcount]); } return(S_OK); } /* * Indicate that host must be power cycled manually. */ static int meatware_reset_req(StonithPlugin * s, int request, const char * host) { int fd, rc; const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to change this, modify meatclient.c as well */ char line[256], meatpipe[256]; char resp_addr[50], resp_mw[50], resp_result[50]; ERRIFWRONGDEV(s,S_OOPS); snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, host); umask(0); unlink(meatpipe); rc = mkfifo(meatpipe, (S_IRUSR | S_IWUSR)); if (rc < 0) { LOG(PIL_CRIT, "cannot create FIFO for Meatware_reset_host"); return S_OOPS; } LOG(PIL_CRIT, "OPERATOR INTERVENTION REQUIRED to reset %s.", host); LOG(PIL_CRIT, "Run \"meatclient -c %s\" AFTER power-cycling the " "machine.", host); fd = open(meatpipe, O_RDONLY); if (fd < 0) { LOG(PIL_CRIT, "cannot open FIFO for Meatware_reset_host"); return S_OOPS; } alarm(600); memset(line, 0, 256); rc = read(fd, line, 256); alarm(0); if (rc < 0) { LOG(PIL_CRIT, "read error on FIFO for Meatware_reset_host"); return S_OOPS; } memset(resp_mw, 0, 50); memset(resp_result, 0, 50); memset(resp_addr, 0, 50); if (sscanf(line, "%s %s %s", resp_mw, resp_result, resp_addr) < 3) { LOG(PIL_CRIT, "Format error - failed to Meatware-reset node %s", host); return S_RESETFAIL; } g_strdown(resp_addr); if (strncmp(resp_mw, "meatware", 8) || strncmp(resp_result, "reply", 5) || strncasecmp(resp_addr, host, strlen(resp_addr))) { LOG(PIL_CRIT, "failed to Meatware-reset node %s", host); return S_RESETFAIL; }else{ LOG(PIL_INFO, "node Meatware-reset: %s", host); unlink(meatpipe); return S_OK; } } /* * Parse the information in the given string * and stash it away... */ static int meatware_set_config(StonithPlugin* s, StonithNVpair *list) { struct pluginDevice* nd; int rc; StonithNamesToGet namestocopy [] = { {ST_HOSTLIST, NULL} , {NULL, NULL} }; ERRIFWRONGDEV(s,S_OOPS); nd = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } rc = Meat_parse_config_info(nd, namestocopy[0].s_value); FREE(namestocopy[0].s_value); return rc; } /* * Return STONITH config vars */ static const char * const * meatware_get_confignames(StonithPlugin* p) { static const char * MeatwareParams[] = {ST_HOSTLIST, NULL }; return MeatwareParams; } /* * Return STONITH info string */ static const char * meatware_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* nd; const char * ret; ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ nd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = nd->idinfo; break; case ST_DEVICENAME: ret = "Your Name Here"; break; case ST_DEVICEDESCR: ret = "Human (meatware) intervention STONITH device.\n" "This STONITH agent prompts a human to reset a machine.\n" "The human tells it when the reset was completed."; break; case ST_CONF_XML: /* XML metadata */ ret = meatwareXML; break; default: ret = NULL; break; } return ret; } /* * Meat Stonith destructor... */ static void meatware_destroy(StonithPlugin *s) { struct pluginDevice* nd; VOIDERRIFWRONGDEV(s); nd = (struct pluginDevice *)s; nd->pluginid = NOTpluginID; if (nd->hostlist) { stonith_free_hostlist(nd->hostlist); nd->hostlist = NULL; } nd->hostcount = -1; FREE(nd); } /* Create a new Meatware Stonith device. */ static StonithPlugin * meatware_new(const char *subplugin) { struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice); if (nd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(nd, 0, sizeof(*nd)); nd->pluginid = pluginid; nd->hostlist = NULL; nd->hostcount = -1; nd->idinfo = DEVICE; nd->sp.s_ops = &meatwareOps; return &(nd->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/null.c0000644000000000000000000001406212120057602026321 0ustar00usergroup00000000000000/* * Stonith module for NULL Stonith device * * Copyright (c) 2000 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "NULL STONITH device" #include "stonith_plugin_common.h" #define PIL_PLUGIN null #define PIL_PLUGIN_S "null" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char ** hostlist; int hostcount; }; static StonithPlugin* null_new(const char *); static void null_destroy(StonithPlugin *); static int null_set_config(StonithPlugin* , StonithNVpair*); static const char * const * null_get_confignames(StonithPlugin*); static const char * null_getinfo(StonithPlugin * s, int InfoType); static int null_status(StonithPlugin * ); static int null_reset_req(StonithPlugin * s , int request, const char * host); static char ** null_hostlist(StonithPlugin *); static struct stonith_ops nullOps ={ null_new, /* Create new STONITH object */ null_destroy, /* Destroy STONITH object */ null_getinfo, /* Return STONITH info string */ null_get_confignames, /* Return list of config params */ null_set_config, /* configure fron NV pairs */ null_status, /* Return STONITH device status */ null_reset_req, /* Request a reset */ null_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &nullOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * Null STONITH device. We are very agreeable, but don't do much :-) */ static const char * pluginid = "nullDevice-Stonith"; static const char * NOTpluginID = "Null device has been destroyed"; #include "stonith_config_xml.h" static const char *nullXML = XML_PARAMETERS_BEGIN XML_HOSTLIST_PARM XML_PARAMETERS_END; static int null_status(StonithPlugin *s) { ERRIFWRONGDEV(s, S_OOPS); return S_OK; } /* * Return the list of hosts configured for this NULL device */ static char ** null_hostlist(StonithPlugin *s) { struct pluginDevice* nd = (struct pluginDevice*)s; ERRIFWRONGDEV(s, NULL); return OurImports->CopyHostList((const char * const *)nd->hostlist); } /* * Pretend to reset the given host on this Stonith device. * (we don't even error check the "request" type) */ static int null_reset_req(StonithPlugin * s, int request, const char * host) { ERRIFWRONGDEV(s,S_OOPS); /* Real devices need to pay attention to the "request" */ /* (but we don't care ;-)) */ LOG(PIL_INFO, "Host null-reset: %s", host); return S_OK; } static const char * const * null_get_confignames(StonithPlugin* p) { static const char * NullParams[] = {ST_HOSTLIST, NULL }; return NullParams; } /* * Parse the config information in the given string, * and stash it away... */ static int null_set_config(StonithPlugin* s, StonithNVpair* list) { struct pluginDevice* nd = (struct pluginDevice*) s; StonithNamesToGet namestocopy [] = { {ST_HOSTLIST, NULL} , {NULL, NULL} }; int rc; ERRIFWRONGDEV(s, S_OOPS); if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } nd->hostlist = OurImports->StringToHostList(namestocopy[0].s_value); FREE(namestocopy[0].s_value); if (nd->hostlist == NULL) { LOG(PIL_CRIT,"StringToHostList() failed"); return S_OOPS; } for (nd->hostcount = 0; nd->hostlist[nd->hostcount] ; nd->hostcount++) { g_strdown(nd->hostlist[nd->hostcount]); } return nd->hostcount ? S_OK : S_BADCONFIG; } static const char * null_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* nd = (struct pluginDevice*) s; const char * ret; ERRIFWRONGDEV(s, NULL); switch (reqtype) { case ST_DEVICEID: ret = nd->idinfo; break; case ST_DEVICENAME: ret = "(nil)"; break; case ST_DEVICEDESCR: ret = "Dummy (do-nothing) STONITH device\n" "FOR TESTING ONLY!"; break; case ST_CONF_XML: /* XML metadata */ ret = nullXML; break; default: ret = NULL; break; } return ret; } /* * NULL Stonith destructor... */ static void null_destroy(StonithPlugin *s) { struct pluginDevice* nd; VOIDERRIFWRONGDEV(s); nd = (struct pluginDevice *)s; nd->pluginid = NOTpluginID; if (nd->hostlist) { stonith_free_hostlist(nd->hostlist); nd->hostlist = NULL; } nd->hostcount = -1; FREE(s); } /* Create a new Null Stonith device. * Too bad this function can't be static */ static StonithPlugin * null_new(const char *subplugin) { struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice); if (nd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(nd, 0, sizeof(*nd)); nd->pluginid = pluginid; nd->idinfo = DEVICE; nd->sp.s_ops = &nullOps; return (StonithPlugin *)nd; } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/nw_rpc100s.c0000644000000000000000000004613312120057602027247 0ustar00usergroup00000000000000/* * Stonith module for Night/Ware RPC100S * * Original code from baytech.c by * Copyright (c) 2000 Alan Robertson * * Modifications for NW RPC100S * Copyright (c) 2000 Computer Generation Incorporated * Eric Z. Ayers * * Mangled by Zhaokai , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "NW RPC100S Power Switch" #include "stonith_plugin_common.h" #define PIL_PLUGIN nw_rpc100s #define PIL_PLUGIN_S "nw_rpc100s" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #define MAX_CFGLINE 256 #include static StonithPlugin * nw_rpc100s_new(const char *); static void nw_rpc100s_destroy(StonithPlugin *); static int nw_rpc100s_set_config(StonithPlugin *, StonithNVpair *); static const char * const * nw_rpc100s_get_confignames(StonithPlugin *); static const char * nw_rpc100s_getinfo(StonithPlugin * s, int InfoType); static int nw_rpc100s_status(StonithPlugin * ); static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host); static char ** nw_rpc100s_hostlist(StonithPlugin *); static struct stonith_ops nw_rpc100sOps ={ nw_rpc100s_new, /* Create new STONITH object */ nw_rpc100s_destroy, /* Destroy STONITH object */ nw_rpc100s_getinfo, /* Return STONITH info string */ nw_rpc100s_get_confignames,/* Return STONITH info string */ nw_rpc100s_set_config, /* Get configuration from NVpairs */ nw_rpc100s_status, /* Return STONITH device status */ nw_rpc100s_reset_req, /* Request a reset */ nw_rpc100s_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_signal.h" #define DOESNT_USE_STONITHKILLCOMM #define DOESNT_USE_STONITHSCANLINE #include "stonith_expect_helpers.h" PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &nw_rpc100sOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* The Nightware RPS-100S is manufactured by: Micro Energetics Corp +1 703 250-3000 http://www.nightware.com/ Thank you to David Hicks of Micro Energetics Corp. for providing a demo unit to write this software. This switch has a very simple protocol, You issue a command and it gives a response. Sample commands are conveniently documented on a sticker on the bottom of the device. The switch accepts a single command of the form //0,yyy,zzz[/m][/h] Where yyy is the wait time before activiting the relay. zzz is the relay time. The default is that the relay is in a default state of ON, which means that usually yyy is the number of seconds to wait before shutting off the power and zzz is the number of seconds the power remains off. There is a dip switch to change the default state to 'OFF'. Don't set this switch. It will screw up this code. An asterisk can be used for zzz to specify an infinite switch time. The /m /and /h command options will convert the specified wait and switch times to either minutewes or hours. A response is either OK or Invalid Entry As far as THIS software is concerned, we have to implement 4 commands: status --> //0,0,BOGUS; # Not a real command, this is just a # probe to see if switch is alive open(on) --> //0,0,0; # turn power to default state (on) close(off) --> //0,0,*; # leave power off indefinitely reboot --> //0,0,10; # immediately turn power off for 10 seconds. and expect the response 'OK' to confirm that the unit is operational. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; int fd; /* FD open to the serial port */ char * device; /* Serial device name to use to communicate to this RPS10 */ char * node; /* Name of the node that this is controlling */ }; /* This string is used to identify this type of object in the config file */ static const char * pluginid = "NW_RPC100S"; static const char * NOTrpcid = "NW RPC100S device has been destroyed"; #include "stonith_config_xml.h" static const char *nw_rpc100sXML = XML_PARAMETERS_BEGIN XML_TTYDEV_PARM XML_HOSTLIST_PARM XML_PARAMETERS_END; /* * Different expect strings that we get from the NW_RPC100S * Remote Power Controllers... */ static struct Etoken NWtokOK[] = { {"OK", 0, 0}, {NULL,0,0}}; static struct Etoken NWtokInvalidEntry[] = { {"Invalid Entry", 0, 0}, {NULL,0,0}}; /* Accept either a CR/NL or an NL/CR */ static struct Etoken NWtokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}}; static int RPCConnect(struct pluginDevice * ctx); static int RPCDisconnect(struct pluginDevice * ctx); static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid); #if defined(ST_POWERON) static int RPCOn(struct pluginDevice*, int unitnum, const char * rebootid); #endif #if defined(ST_POWEROFF) static int RPCOff(struct pluginDevice*, int unitnum, const char * rebootid); #endif static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host ); /*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info);*/ #define SENDCMD(cmd, timeout) { \ int return_val = RPCSendCommand(ctx, cmd, timeout); \ if (return_val != S_OK) { \ return return_val; \ } \ } /* * RPCSendCommand - send a command to the specified outlet */ static int RPCSendCommand (struct pluginDevice *ctx, const char *command, int timeout) { char writebuf[64]; /* All commands are short. They should be WAY LESS than 64 chars long! */ int return_val; /* system call result */ fd_set rfds, wfds, xfds; /* list of FDs for select() */ struct timeval tv; /* */ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); snprintf (writebuf, sizeof(writebuf), "%s\r", command); if (Debug) { LOG(PIL_DEBUG, "Sending %s", writebuf); } /* Make sure the serial port won't block on us. use select() */ FD_SET(ctx->fd, &wfds); FD_SET(ctx->fd, &xfds); tv.tv_sec = timeout; tv.tv_usec = 0; return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv); if (return_val == 0) { /* timeout waiting on serial port */ LOG(PIL_CRIT, "%s: Timeout writing to %s" , pluginid, ctx->device); return S_TIMEOUT; } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) { /* an error occured */ LOG(PIL_CRIT, "%s: Error before writing to %s: %s" , pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* send the command */ if (write(ctx->fd, writebuf, strlen(writebuf)) != (int)strlen(writebuf)) { LOG(PIL_CRIT, "%s: Error writing to %s : %s" , pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* suceeded! */ return S_OK; } /* end RPCSendCommand() */ /* * RPCReset - Reset (power-cycle) the given outlet number * * This device can only control one power outlet - unitnum is ignored. * */ static int RPCReset(struct pluginDevice* ctx, int unitnum, const char * rebootid) { if (Debug) { LOG(PIL_DEBUG, "Calling RPCReset (%s)", pluginid); } if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid , ctx->device); return S_OOPS; } /* send the "toggle power" command */ SENDCMD("//0,0,10;\r\n", 12); /* Expect "OK" */ EXPECT(ctx->fd, NWtokOK, 5); if (Debug) { LOG(PIL_DEBUG, "Got OK"); } EXPECT(ctx->fd, NWtokCRNL, 2); if (Debug) { LOG(PIL_DEBUG, "Got NL"); } return(S_OK); } /* end RPCReset() */ #if defined(ST_POWERON) /* * RPCOn - Turn OFF the given outlet number */ static int RPCOn(struct pluginDevice* ctx, int unitnum, const char * host) { if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid , ctx->device); return S_OOPS; } /* send the "On" command */ SENDCMD("//0,0,0;\r\n", 10); /* Expect "OK" */ EXPECT(ctx->fd, NWtokOK, 5); EXPECT(ctx->fd, NWtokCRNL, 2); return(S_OK); } /* end RPCOn() */ #endif #if defined(ST_POWEROFF) /* * RPCOff - Turn Off the given outlet number */ static int RPCOff(struct pluginDevice* ctx, int unitnum, const char * host) { if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid , ctx->device); return S_OOPS; } /* send the "Off" command */ SENDCMD("//0,0,*;\r\n", 10); /* Expect "OK" */ EXPECT(ctx->fd, NWtokOK, 5); EXPECT(ctx->fd, NWtokCRNL, 2); return(S_OK); } /* end RPCOff() */ #endif /* * nw_rpc100s_status - API entry point to probe the status of the stonith device * (basically just "is it reachable and functional?", not the * status of the individual outlets) * * Returns: * S_OOPS - some error occured * S_OK - if the stonith device is reachable and online. */ static int nw_rpc100s_status(StonithPlugin *s) { struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "Calling nw_rpc100s_status (%s)", pluginid); } ERRIFNOTCONFIGED(s,S_OOPS); ctx = (struct pluginDevice*) s; if (RPCConnect(ctx) != S_OK) { return(S_OOPS); } /* The "connect" really does enough work to see if the controller is alive... It verifies that it is returning RPS-10 Ready */ return(RPCDisconnect(ctx)); } /* * nw_rpc100s_hostlist - API entry point to return the list of hosts * for the devices on this NW_RPC100S unit * * This type of device is configured from the config file, * so we don't actually have to connect to figure this * out, just peruse the 'ctx' structure. * Returns: * NULL on error * a malloced array, terminated with a NULL, * of null-terminated malloc'ed strings. */ static char ** nw_rpc100s_hostlist(StonithPlugin *s) { char ** ret = NULL; /* list to return */ struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "Calling nw_rpc100s_hostlist (%s)", pluginid); } ERRIFNOTCONFIGED(s,NULL); ctx = (struct pluginDevice*) s; ret = OurImports->StringToHostList(ctx->node); if (ret == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); } else { g_strdown(ret[0]); } return(ret); } /* end si_hostlist() */ /* * Parse the given configuration information, and stash it away... * * contains the parameters specific to this type of object * * The format of for this module is: * [ ] ... * * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through * a device attached to serial port /dev/ttyS0. * A machine named 'nodeb' can kill machines 'nodea' and 'nodec' * through a device attached to serial port /dev/ttyS1 (outlets 0 * and 1 respectively) * * stonith nodea NW_RPC100S /dev/ttyS0 nodeb 0 * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 nodec 1 * * Another possible configuration is for 2 stonith devices accessible * through 2 different serial ports on nodeb: * * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 * stonith nodeb NW_RPC100S /dev/ttyS1 nodec 0 */ /*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info) { }*/ /* * RPCConnect - * * Connect to the given NW_RPC100S device. * Side Effects * ctx->fd now contains a valid file descriptor to the serial port * ??? LOCK THE SERIAL PORT ??? * * Returns * S_OK on success * S_OOPS on error * S_TIMEOUT if the device did not respond * */ static int RPCConnect(struct pluginDevice * ctx) { /* Open the serial port if it isn't already open */ if (ctx->fd < 0) { struct termios tio; if (OurImports->TtyLock(ctx->device) < 0) { LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid); return S_OOPS; } ctx->fd = open (ctx->device, O_RDWR); if (ctx->fd <0) { LOG(PIL_CRIT, "%s: Can't open %s : %s" , pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* set the baudrate to 9600 8 - N - 1 */ memset (&tio, 0, sizeof(tio)); /* ??? ALAN - the -tradtitional flag on gcc causes the CRTSCTS constant to generate a warning, and warnings are treated as errors, so I can't set this flag! - EZA ??? Hmmm. now that I look at the documentation, RTS is just wired high on this device! we don't need it. */ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ; tio.c_lflag = ICANON; if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) { LOG(PIL_CRIT, "%s: Can't set attributes %s : %s" , pluginid, ctx->device, strerror(errno)); close (ctx->fd); OurImports->TtyUnlock(ctx->device); ctx->fd=-1; return S_OOPS; } /* flush all data to and fro the serial port before we start */ if (tcflush (ctx->fd, TCIOFLUSH) < 0) { LOG(PIL_CRIT, "%s: Can't flush %s : %s" , pluginid, ctx->device, strerror(errno)); close (ctx->fd); OurImports->TtyUnlock(ctx->device); ctx->fd=-1; return S_OOPS; } } /* Send a BOGUS string */ SENDCMD("//0,0,BOGUS;\r\n", 10); /* Should reply with "Invalid Command" */ if (Debug) { LOG(PIL_DEBUG, "Waiting for \"Invalid Entry\""); } EXPECT(ctx->fd, NWtokInvalidEntry, 12); if (Debug) { LOG(PIL_DEBUG, "Got Invalid Entry"); } EXPECT(ctx->fd, NWtokCRNL, 2); if (Debug) { LOG(PIL_DEBUG, "Got NL"); } return(S_OK); } static int RPCDisconnect(struct pluginDevice * ctx) { if (ctx->fd >= 0) { /* Flush the serial port, we don't care what happens to the characters and failing to do this can cause close to hang. */ tcflush(ctx->fd, TCIOFLUSH); close (ctx->fd); if (ctx->device != NULL) { OurImports->TtyUnlock(ctx->device); } } ctx->fd = -1; return S_OK; } /* * RPCNametoOutlet - Map a hostname to an outlet number on this stonith device. * * Returns: * 0 on success ( the outlet number on the RPS10 - there is only one ) * -1 on failure (host not found in the config file) * */ static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host ) { int rc = -1; if (!strcasecmp(ctx->node, host)) { rc = 0; } return rc; } /* * nw_rpc100s_reset - API call to Reset (reboot) the given host on * this Stonith device. This involves toggling the power off * and then on again, OR just calling the builtin reset command * on the stonith device. */ static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host) { int rc = S_OK; int lorc = S_OK; int outletnum = -1; struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "Calling nw_rpc100s_reset (%s)", pluginid); } ERRIFNOTCONFIGED(s,S_OOPS); ctx = (struct pluginDevice*) s; if ((rc = RPCConnect(ctx)) != S_OK) { return(rc); } outletnum = RPCNametoOutlet(ctx, host); LOG(PIL_DEBUG, "zk:outletname=%d", outletnum); if (outletnum < 0) { LOG(PIL_WARN, "%s doesn't control host [%s]" , ctx->device, host); RPCDisconnect(ctx); return(S_BADHOST); } switch(request) { #if defined(ST_POWERON) case ST_POWERON: rc = RPCOn(ctx, outletnum, host); break; #endif #if defined(ST_POWEROFF) case ST_POWEROFF: rc = RPCOff(ctx, outletnum, host); break; #endif case ST_GENERIC_RESET: rc = RPCReset(ctx, outletnum, host); break; default: rc = S_INVAL; break; } lorc = RPCDisconnect(ctx); return(rc != S_OK ? rc : lorc); } /* * Parse the information in the given string * and stash it away... */ static int nw_rpc100s_set_config(StonithPlugin* s, StonithNVpair *list) { struct pluginDevice* ctx; StonithNamesToGet namestocopy [] = { {ST_TTYDEV, NULL} , {ST_HOSTLIST, NULL} , {NULL, NULL} }; int rc; ERRIFWRONGDEV(s,S_OOPS); if (s->isconfigured) { return S_OOPS; } ctx = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } ctx->device = namestocopy[0].s_value; ctx->node = namestocopy[1].s_value; return S_OK; } /* * Return STONITH config vars */ static const char * const * nw_rpc100s_get_confignames(StonithPlugin* p) { static const char * RpcParams[] = {ST_TTYDEV , ST_HOSTLIST, NULL }; return RpcParams; } /* * nw_rpc100s_getinfo - API entry point to retrieve something from the handle */ static const char * nw_rpc100s_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* ctx; const char * ret; ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ ctx = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = ctx->idinfo; break; case ST_DEVICENAME: ret = ctx->device; break; case ST_DEVICEDESCR: ret = "Micro Energetics Night/Ware RPC100S"; break; case ST_DEVICEURL: ret = "http://www.microenergeticscorp.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = nw_rpc100sXML; break; default: ret = NULL; break; } return ret; } /* * nw_rpc100s_destroy - API entry point to destroy a NW_RPC100S Stonith object. */ static void nw_rpc100s_destroy(StonithPlugin *s) { struct pluginDevice* ctx; VOIDERRIFWRONGDEV(s); ctx = (struct pluginDevice *)s; ctx->pluginid = NOTrpcid; /* close the fd if open and set ctx->fd to invalid */ RPCDisconnect(ctx); if (ctx->device != NULL) { FREE(ctx->device); ctx->device = NULL; } if (ctx->node != NULL) { FREE(ctx->node); ctx->node = NULL; } FREE(ctx); } /* * nw_rpc100s_new - API entry point called to create a new NW_RPC100S Stonith * device object. */ static StonithPlugin * nw_rpc100s_new(const char *subplugin) { struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice); if (ctx == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(ctx, 0, sizeof(*ctx)); ctx->pluginid = pluginid; ctx->fd = -1; ctx->device = NULL; ctx->node = NULL; ctx->idinfo = DEVICE; ctx->sp.s_ops = &nw_rpc100sOps; return &(ctx->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/rcd_serial.c0000644000000000000000000003655512120057602027471 0ustar00usergroup00000000000000/* * Stonith module for RCD_SERIAL Stonith device * * Original code from null.c by * Copyright (c) 2000 Alan Robertson * * Copious borrowings from nw_rpc100s.c by * Copyright (c) 2000 Computer Generation Incorporated * Eric Z. Ayers * * and from apcsmart.c by * Copyright (c) 2000 Andreas Piesk * * Modifications for RC Delayed Serial Ciruit by * Copyright (c) 2002 John Sutton * * Mangled by Zhaokai , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "RC Delayed Serial" #include "stonith_plugin_common.h" #include "stonith_signal.h" #define PIL_PLUGIN rcd_serial #define PIL_PLUGIN_S "rcd_serial" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #define ST_DTRRTS "dtr_rts" #define ST_MSDURATION "msduration" #define MAX_RCD_SERIALLINE 512 #include #include #include static StonithPlugin* rcd_serial_new(const char *); static void rcd_serial_destroy(StonithPlugin *); static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *); static const char * const * rcd_serial_get_confignames(StonithPlugin *); static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType); static int rcd_serial_status(StonithPlugin * ); static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host); static char ** rcd_serial_hostlist(StonithPlugin *); static struct stonith_ops rcd_serialOps ={ rcd_serial_new, /* Create new STONITH object */ rcd_serial_destroy, /* Destroy STONITH object */ rcd_serial_getinfo, /* Return STONITH info string */ rcd_serial_get_confignames,/* Return STONITH info string */ rcd_serial_set_config, /* Get configuration from NVpairs */ rcd_serial_status, /* Return STONITH device status */ rcd_serial_reset_req, /* Request a reset */ rcd_serial_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &rcd_serialOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* ------------------- RCD specific stuff -------------- */ /* A diagram of a circuit suitable for use with this plugin is in README.rcd_serial which should be somewhere in the distribution (if Alan includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember to put it there ;-). Once you've got this built, you can test things using the stonith command as follows: stonith -L will show a list of plugin types, including rcd_serial stonith -t rcd_serial testhost will show required parameters In these 3 you can either pass the params after the -p option or you can put them in a config file and use -F configname instead of -p "param ...". stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S will show the status of the device stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l will list the single host testhost stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost will reset testhost (provided testhost has its reset pins suitably wired to the RTS signal coming out of port /dev/ttyS0 and that 1.5s is enough time to cause a reset ;-) */ /* Define RCD_NOPAUSE if you are using the serial port for some purpose _in_addition_ to using it as a stonith device. For example, I use one of the input pins on the same serial port for monitoring the state of a power supply. Periodically, a cron job has to open the port to read the state of this input and thus has to clear down the output pins DTR and RTS in order to avoid causing a spurious stonith reset. Now, if it should happen that just at the same time as we are _really_ trying to do a stonith reset, this cron job starts up, then the stonith reset won't occur ;-(. To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE. The effect of this is that instead of setting the line high just once and then falling into a pause until an alarm goes off, rather, the program falls into a loop which is continuously setting the line high. That costs us a bit of CPU as compared with sitting in a pause, but hey, how often is this code going to get exercised! Never, we hope... */ #undef RCD_NOPAUSE #ifdef RCD_NOPAUSE static int RCD_alarmcaught; #endif /* * own prototypes */ static void RCD_alarm_handler(int sig); static int RCD_open_serial_port(char *device); static int RCD_close_serial_port(char *device, int fd); static void RCD_alarm_handler(int sig) { #if !defined(HAVE_POSIX_SIGNALS) if (sig) { signal(sig, SIG_DFL); }else{ signal(sig, RCD_alarm_handler); } #else struct sigaction sa; sigset_t sigmask; /* Maybe a bit naughty but it works and it saves duplicating all */ /* this setup code - if handler called with 0 for sig, we install */ /* ourself as handler. */ if (sig) { sa.sa_handler = (void (*)(int))SIG_DFL; }else{ sa.sa_handler = RCD_alarm_handler; } sigemptyset(&sigmask); sa.sa_mask = sigmask; sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); #endif #ifdef RCD_NOPAUSE RCD_alarmcaught = 1; #endif return; } static int RCD_open_serial_port(char *device) { int fd; int status; int bothbits; if (OurImports->TtyLock(device) < 0) { if (Debug) { LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__); } return -1; } bothbits = TIOCM_RTS | TIOCM_DTR; if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) { /* Opening the device always sets DTR & CTS high. Clear them down immediately. */ status = ioctl(fd, TIOCMBIC, &bothbits); /* If there was an error clearing bits, set the fd to -1 * ( indicates error ) */ if (status != 0 ) { fd = -1; } } return fd; } static int RCD_close_serial_port(char *device, int fd) { int rc = close(fd); if (device != NULL) { OurImports->TtyUnlock(device); } return rc; } /* * RCD_Serial STONITH device. */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char ** hostlist; /* name of single host we can reset */ int hostcount; /* i.e. 1 after initialisation */ char * device; /* serial device name */ char * signal; /* either rts or dtr */ long msduration; /* how long (ms) to assert the signal */ }; static const char * pluginid = "RCD_SerialDevice-Stonith"; static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed"; #include "stonith_config_xml.h" #define XML_DTRRTS_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_DTRRTS \ XML_PARM_SHORTDESC_END #define XML_DTRRTS_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \ XML_PARM_LONGDESC_END #define XML_DTRRTS_PARM \ XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1") \ XML_DTRRTS_SHORTDESC \ XML_DTRRTS_LONGDESC \ XML_PARAMETER_END #define XML_MSDURATION_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_MSDURATION \ XML_PARM_SHORTDESC_END #define XML_MSDURATION_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \ XML_PARM_LONGDESC_END #define XML_MSDURATION_PARM \ XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1") \ XML_MSDURATION_SHORTDESC \ XML_MSDURATION_LONGDESC \ XML_PARAMETER_END static const char *rcd_serialXML = XML_PARAMETERS_BEGIN XML_HOSTLIST_PARM XML_TTYDEV_PARM XML_DTRRTS_PARM XML_MSDURATION_PARM XML_PARAMETERS_END; static int rcd_serial_status(StonithPlugin *s) { struct pluginDevice* rcd; int fd; const char * err; ERRIFWRONGDEV(s,S_OOPS); rcd = (struct pluginDevice*) s; /* All we can do is make sure the serial device exists and can be opened and closed without error. */ if ((fd = RCD_open_serial_port(rcd->device)) == -1) { err = strerror(errno); LOG(PIL_CRIT, "%s: open of %s failed - %s", __FUNCTION__, rcd->device, err); return(S_OOPS); } if (RCD_close_serial_port(rcd->device, fd) != 0) { err = strerror(errno); LOG(PIL_CRIT, "%s: close of %s failed - %s", __FUNCTION__, rcd->device, err); return(S_OOPS); } return S_OK; } /* * Return the list of hosts configured for this RCD_SERIAL device */ static char ** rcd_serial_hostlist(StonithPlugin *s) { struct pluginDevice* rcd; ERRIFWRONGDEV(s,NULL); rcd = (struct pluginDevice*) s; if (rcd->hostcount < 0) { LOG(PIL_CRIT , "unconfigured stonith object in RCD_SERIAL_list_hosts"); return(NULL); } return OurImports->CopyHostList((const char * const *)rcd->hostlist); } /* * At last, we really do it! I don't know what the request argument * is so am just ignoring it... */ static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host) { struct pluginDevice* rcd; int fd; int sigbit; struct itimerval timer; const char * err; ERRIFWRONGDEV(s,S_OOPS); rcd = (struct pluginDevice *) s; /* check that host matches */ if (strcasecmp(host, rcd->hostlist[0])) { LOG(PIL_CRIT, "%s: host '%s' not in hostlist.", __FUNCTION__, host); return(S_BADHOST); } /* Set the appropriate bit for the signal */ sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR; /* Set up the timer */ timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value.tv_sec = rcd->msduration / 1000; timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000; /* Open the device */ if ((fd = RCD_open_serial_port(rcd->device)) == -1) { #ifdef HAVE_STRERROR err = strerror(errno); #else err = sys_errlist[errno]; #endif LOG(PIL_CRIT, "%s: open of %s failed - %s", __FUNCTION__, rcd->device, err); return(S_OOPS); } /* Start the timer */ RCD_alarm_handler(0); #ifdef RCD_NOPAUSE RCD_alarmcaught = 0; #endif setitimer(ITIMER_REAL, &timer, 0); /* Set the line high */ ioctl(fd, TIOCMBIS, &sigbit); /* Wait for the alarm signal */ #ifdef RCD_NOPAUSE while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit); #else pause(); #endif /* Clear the line low */ ioctl(fd, TIOCMBIC, &sigbit); /* Close the port */ if (RCD_close_serial_port(rcd->device, fd) != 0) { err = strerror(errno); LOG(PIL_CRIT, "%s: close of %s failed - %s", __FUNCTION__, rcd->device, err); return(S_OOPS); } LOG(PIL_INFO,"Host rcd_serial-reset: %s", host); return S_OK; } /* * Parse the information in the given string * and stash it away... */ static int rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list) { struct pluginDevice* rcd; StonithNamesToGet namestocopy [] = { {ST_HOSTLIST, NULL} , {ST_TTYDEV, NULL} , {ST_DTRRTS, NULL} , {ST_MSDURATION, NULL} , {NULL, NULL} }; char *endptr; int rc = 0; LOG(PIL_DEBUG, "%s:called", __FUNCTION__); ERRIFWRONGDEV(s,S_OOPS); if (s->isconfigured) { return S_OOPS; } rcd = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) { LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__); FREE(namestocopy[0].s_value); FREE(namestocopy[1].s_value); FREE(namestocopy[2].s_value); FREE(namestocopy[3].s_value); return S_OOPS; } rcd->hostlist[0] = namestocopy[0].s_value; g_strdown(rcd->hostlist[0]); rcd->hostlist[1] = NULL; rcd->hostcount = 1; rcd->device = namestocopy[1].s_value; rcd->signal = namestocopy[2].s_value; if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) { LOG(PIL_CRIT, "%s: Invalid signal name '%s'", pluginid, rcd->signal); FREE(namestocopy[3].s_value); return S_BADCONFIG; } errno = 0; rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0); if (((errno == ERANGE) && (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX)) || *endptr != 0 || rcd->msduration < 1) { LOG(PIL_CRIT, "%s: Invalid msduration '%s'", pluginid, namestocopy[3].s_value); FREE(namestocopy[3].s_value); return S_BADCONFIG; } FREE(namestocopy[3].s_value); return S_OK; } /* * Return STONITH config vars */ static const char * const * rcd_serial_get_confignames(StonithPlugin* p) { static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV , ST_DTRRTS, ST_MSDURATION, NULL }; return RcdParams; } /* * Return STONITH info string */ static const char * rcd_serial_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* rcd; const char * ret; ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ rcd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = rcd->idinfo; break; case ST_DEVICENAME: ret = rcd->device; break; case ST_DEVICEDESCR: ret = "RC Delayed Serial STONITH Device\n" "This device can be constructed cheaply from" " readily available components,\n" "with sufficient expertise and testing.\n" "See README.rcd_serial for circuit diagram.\n"; break; case ST_DEVICEURL: ret = "http://www.scl.co.uk/rcd_serial/"; break; case ST_CONF_XML: /* XML metadata */ ret = rcd_serialXML; break; default: ret = NULL; break; } return ret; } /* * RCD_SERIAL Stonith destructor... */ static void rcd_serial_destroy(StonithPlugin *s) { struct pluginDevice* rcd; VOIDERRIFWRONGDEV(s); rcd = (struct pluginDevice *)s; rcd->pluginid = NOTrcd_serialID; if (rcd->hostlist) { stonith_free_hostlist(rcd->hostlist); rcd->hostlist = NULL; } rcd->hostcount = -1; if (rcd->device) { FREE(rcd->device); } if (rcd->signal) { FREE(rcd->signal); } FREE(rcd); } /* * Create a new RCD_Serial Stonith device. * Too bad this function can't be static. (Hmm, weird, it _is_ static?) */ static StonithPlugin * rcd_serial_new(const char *subplugin) { struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice); if (rcd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(rcd, 0, sizeof(*rcd)); rcd->pluginid = pluginid; rcd->hostlist = NULL; rcd->hostcount = -1; rcd->device = NULL; rcd->signal = NULL; rcd->msduration = 0; rcd->idinfo = DEVICE; rcd->sp.s_ops = &rcd_serialOps; return &(rcd->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/rhcs.c0000644000000000000000000005465212120057602026317 0ustar00usergroup00000000000000/* * Stonith module for RedHat Cluster Suite fencing plugins * * Copyright (c) 2001 SuSE Linux AG * Portions Copyright (c) 2004, tummy.com, ltd. * * Based on ssh.c, Authors: Joachim Gleissner , * Lars Marowsky-Bree * Modified for external.c: Scott Kleihege * Reviewed, tested, and config parsing: Sean Reifschneider * And overhauled by Lars Marowsky-Bree , so the circle * closes... * Mangled by Zhaokai , IBM, 2005 * Changed to allow full-featured external plugins by Dave Blaschke * * Modified for rhcs.c: Dejan Muhamedagic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include "stonith_plugin_common.h" #define PIL_PLUGIN rhcs #define PIL_PLUGIN_S "rhcs" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * rhcs_new(const char *); static void rhcs_destroy(StonithPlugin *); static int rhcs_set_config(StonithPlugin *, StonithNVpair *); static const char * const * rhcs_get_confignames(StonithPlugin *); static const char * rhcs_getinfo(StonithPlugin * s, int InfoType); static int rhcs_status(StonithPlugin * ); static int rhcs_reset_req(StonithPlugin * s, int request, const char * host); static char ** rhcs_hostlist(StonithPlugin *); static struct stonith_ops rhcsOps ={ rhcs_new, /* Create new STONITH object */ rhcs_destroy, /* Destroy STONITH object */ rhcs_getinfo, /* Return STONITH info string */ rhcs_get_confignames, /* Return STONITH info string */ rhcs_set_config, /* Get configuration from NVpairs */ rhcs_status, /* Return STONITH device status */ rhcs_reset_req, /* Request a reset */ rhcs_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &rhcsOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * RHCS STONITH device */ struct pluginDevice { StonithPlugin sp; const char * pluginid; GHashTable * cmd_opts; char * subplugin; char ** confignames; char * hostlist; char * outputbuf; xmlDoc * metadata; }; static const char * pluginid = "RHCSDevice-Stonith"; static const char * NOTpluginID = "RHCS device has been destroyed"; /* Prototypes */ /* Run the command with op and return the exit status + the output * (NULL -> discard output) */ static int rhcs_run_cmd(struct pluginDevice *sd, const char *op, const char *host, char **output); /* Just free up the configuration and the memory, if any */ static void rhcs_unconfig(struct pluginDevice *sd); static int rhcs_status(StonithPlugin *s) { struct pluginDevice * sd; const char * op = "monitor"; int rc; char * output = NULL; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } rc = rhcs_run_cmd(sd, op, NULL, &output); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); if (output) { LOG(PIL_CRIT, "plugin output: %s", output); } } else { if (Debug) { LOG(PIL_DEBUG, "%s: running '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } } if (output) { FREE(output); } return rc; } static int get_num_tokens(char *str) { int namecount = 0; if (!str) return namecount; while (*str != EOS) { str += strspn(str, WHITESPACE); if (*str == EOS) break; str += strcspn(str, WHITESPACE); namecount++; } return namecount; } static char ** rhcs_hostlist(StonithPlugin *s) { struct pluginDevice* sd; const char * op = "gethosts"; int i, namecount; char ** ret; char * tmp; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,NULL); sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(NULL); } namecount = get_num_tokens(sd->hostlist); ret = MALLOC((namecount+1)*sizeof(char *)); if (!ret) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return NULL; } memset(ret, 0, (namecount+1)*sizeof(char *)); /* White-space split the sd->hostlist here */ i = 0; tmp = strtok(sd->hostlist, WHITESPACE); while (tmp != NULL) { if (Debug) { LOG(PIL_DEBUG, "%s: %s host %s", __FUNCTION__, sd->subplugin, tmp); } ret[i] = STRDUP(tmp); if (!ret[i]) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); stonith_free_hostlist(ret); return NULL; } i++; tmp = strtok(NULL, WHITESPACE); } if (i == 0) { LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist", __FUNCTION__, sd->subplugin, op); stonith_free_hostlist(ret); ret = NULL; } return(ret); } static int rhcs_reset_req(StonithPlugin * s, int request, const char * host) { struct pluginDevice * sd; const char * op; int rc; char * output = NULL; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); if (Debug) { LOG(PIL_DEBUG, "Host rhcs-reset initiating on %s", host); } sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } switch (request) { case ST_GENERIC_RESET: op = "reboot"; break; case ST_POWEROFF: op = "off"; break; case ST_POWERON: op = "on"; break; default: LOG(PIL_CRIT, "%s: Unknown stonith request %d", __FUNCTION__, request); return S_OOPS; break; } rc = rhcs_run_cmd(sd, op, host, &output); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d", __FUNCTION__, sd->subplugin, op, host, rc); if (output) { LOG(PIL_CRIT, "plugin output: %s", output); FREE(output); } return S_RESETFAIL; } else { if (Debug) { LOG(PIL_DEBUG, "%s: running '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } if (output) { LOG(PIL_INFO, "plugin output: %s", output); FREE(output); } return S_OK; } } static int rhcs_parse_config_info(struct pluginDevice* sd, StonithNVpair * info) { char * key; char * value; StonithNVpair * nv; sd->hostlist = NULL; sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal); /* TODO: Maybe treat "" as delimeters too so * whitespace can be passed to the plugins... */ for (nv = info; nv->s_name; nv++) { if (!nv->s_name || !nv->s_value) { continue; } key = STRDUP(nv->s_name); if (!key) { goto err_mem; } value = STRDUP(nv->s_value); if (!value) { FREE(key); goto err_mem; } if (!strcmp(key,"hostlist")) { sd->hostlist = value; FREE(key); } else { g_hash_table_insert(sd->cmd_opts, key, value); } } return(S_OK); err_mem: LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__); rhcs_unconfig(sd); return(S_OOPS); } static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data) { if (key) { FREE(key); } if (value) { FREE(value); } return TRUE; } static void rhcs_unconfig(struct pluginDevice *sd) { if (sd->cmd_opts) { g_hash_table_foreach_remove(sd->cmd_opts, let_remove_eachitem, NULL); g_hash_table_destroy(sd->cmd_opts); sd->cmd_opts = NULL; } if (sd->hostlist) { FREE(sd->hostlist); sd->hostlist = NULL; } if (sd->metadata) { xmlFreeDoc(sd->metadata); xmlCleanupParser(); sd->metadata = NULL; } } /* * Parse the information in the given string * and stash it away... */ static int rhcs_set_config(StonithPlugin* s, StonithNVpair *list) { struct pluginDevice * sd; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); /* make sure that command has not already been set */ if (s->isconfigured) { return(S_OOPS); } sd = (struct pluginDevice*) s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(S_OOPS); } #if 0 /* the required parameters may be acquired from the metadata * */ if (sd->confignames == NULL) { /* specified by name=value pairs, check required parms */ if (rhcs_get_confignames(s) == NULL) { return(S_OOPS); } for (p = sd->confignames; *p; p++) { if (OurImports->GetValue(list, *p) == NULL) { LOG(PIL_INFO, "Cannot get parameter %s from " "StonithNVpair", *p); } } } #endif return rhcs_parse_config_info(sd, list); } /* Only interested in regular files starting with fence_ that are also executable */ static int rhcs_exec_select(const struct dirent *dire) { struct stat statf; char filename[FILENAME_MAX]; int rc; rc = snprintf(filename, FILENAME_MAX, "%s/%s", STONITH_RHCS_PLUGINDIR, dire->d_name); if (rc <= 0 || rc >= FILENAME_MAX) { return 0; } if ((stat(filename, &statf) == 0) && (S_ISREG(statf.st_mode)) && (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) { if (statf.st_mode & (S_IWGRP|S_IWOTH)) { LOG(PIL_WARN, "Executable file %s ignored " "(writable by group/others)", filename); return 0; }else{ return 1; } } return 0; } static xmlDoc * load_metadata(struct pluginDevice * sd) { xmlDoc *doc = NULL; const char *op = "metadata"; int rc; char *ret = NULL; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } rc = rhcs_run_cmd(sd, op, NULL, &ret); if (rc != 0) { LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d", __FUNCTION__, sd->subplugin, op, rc); if (ret) { LOG(PIL_CRIT, "plugin output: %s", ret); FREE(ret); } goto err; } if (Debug) { LOG(PIL_DEBUG, "%s: '%s %s' returned %d", __FUNCTION__, sd->subplugin, op, rc); } doc = xmlParseMemory(ret, strlen(ret)); if (!doc) { LOG(PIL_CRIT, "%s: could not parse metadata", __FUNCTION__); goto err; } sd->metadata = doc; err: if (ret) { FREE(ret); } return doc; } static const char *skip_attrs[] = { "action", "verbose", "debug", "version", "help", "separator", NULL }; /* XML stuff */ typedef int (*node_proc) (xmlNodeSet *nodes, struct pluginDevice *sd); static int proc_xpath(const char *xpathexp, struct pluginDevice *sd, node_proc fun) { xmlXPathObject *xpathObj = NULL; xmlXPathContext *xpathCtx = NULL; int rc = 1; if (!sd->metadata && !load_metadata(sd)) { LOG(PIL_INFO, "%s: no metadata", __FUNCTION__); return 1; } /* Create xpath evaluation context */ xpathCtx = xmlXPathNewContext(sd->metadata); if(xpathCtx == NULL) { LOG(PIL_CRIT, "%s: unable to create new XPath context", __FUNCTION__); return 1; } /* Evaluate xpath expression */ xpathObj = xmlXPathEvalExpression((const xmlChar*)xpathexp, xpathCtx); if(xpathObj == NULL) { LOG(PIL_CRIT, "%s: unable to evaluate expression %s", __FUNCTION__, xpathexp); goto err; } if (sd->outputbuf != NULL) { FREE(sd->outputbuf); sd->outputbuf = NULL; } rc = fun(xpathObj->nodesetval, sd); err: if (xpathObj) xmlXPathFreeObject(xpathObj); if (xpathCtx) xmlXPathFreeContext(xpathCtx); return rc; } static int load_confignames(xmlNodeSet *nodes, struct pluginDevice *sd) { xmlChar *attr; const char * const*skip; xmlNode *cur; int i, j, namecount; namecount = nodes->nodeNr; if (!namecount) { LOG(PIL_INFO, "%s: no configuration parameters", __FUNCTION__); return 1; } sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *)); if (sd->confignames == NULL) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return 1; } /* now copy over confignames */ j = 0; for (i = 0; i < nodes->nodeNr; i++) { cur = nodes->nodeTab[i]; attr = xmlGetProp(cur, (const xmlChar*)"name"); for (skip = skip_attrs; *skip; skip++) { if (!strcmp(*skip,(char *)attr)) goto skip; } if (Debug) { LOG(PIL_DEBUG, "%s: %s configname %s", __FUNCTION__, sd->subplugin, (char *)attr); } sd->confignames[j++] = strdup((char *)attr); xmlFree(attr); skip: continue; } sd->confignames[j] = NULL; return 0; } static int dump_content(xmlNodeSet *nodes, struct pluginDevice *sd) { xmlChar *content = NULL; xmlNode *cur; int rc = 1; if (!nodes || !nodes->nodeTab || !nodes->nodeTab[0]) { LOG(PIL_WARN, "%s: %s no nodes", __FUNCTION__, sd->subplugin); return 1; } cur = nodes->nodeTab[0]; content = xmlNodeGetContent(cur); if (content && strlen((char *)content) > 0) { if (Debug) { LOG(PIL_DEBUG, "%s: %s found content for %s", __FUNCTION__, sd->subplugin, cur->name); } sd->outputbuf = STRDUP((char *)content); rc = !(*sd->outputbuf); } else { if (Debug) { LOG(PIL_DEBUG, "%s: %s no content for %s", __FUNCTION__, sd->subplugin, cur->name); } rc = 1; } if (content) xmlFree(content); return rc; } static int dump_params_xml(xmlNodeSet *nodes, struct pluginDevice *sd) { int len = 0; xmlNode *cur; xmlBuffer *xml_buffer = NULL; int rc = 0; xml_buffer = xmlBufferCreate(); if (!xml_buffer) { LOG(PIL_CRIT, "%s: failed to create xml buffer", __FUNCTION__); return 1; } cur = nodes->nodeTab[0]; len = xmlNodeDump(xml_buffer, sd->metadata, cur, 0, TRUE); if (len <= 0) { LOG(PIL_CRIT, "%s: could not dump xml for %s", __FUNCTION__, (char *)xmlGetProp(cur, (const xmlChar*)"name")); rc = 1; goto err; } sd->outputbuf = STRDUP((char *)xml_buffer->content); err: xmlBufferFree(xml_buffer); return rc; } /* * Return STONITH config vars */ static const char * const * rhcs_get_confignames(StonithPlugin* p) { struct pluginDevice * sd; int i; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } sd = (struct pluginDevice *)p; if (sd->subplugin != NULL) { if (!sd->metadata && !load_metadata(sd)) { return NULL; } proc_xpath("/resource-agent/parameters/parameter", sd, load_confignames); } else { /* return list of subplugins in rhcs directory */ struct dirent ** files = NULL; int dircount; /* get the rhcs plugin's confignames (list of subplugins) */ dircount = scandir(STONITH_RHCS_PLUGINDIR, &files, SCANSEL_CAST rhcs_exec_select, NULL); if (dircount < 0) { return NULL; } sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *)); if (!sd->confignames) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return NULL; } for (i = 0; i < dircount; i++) { sd->confignames[i] = STRDUP(files[i]->d_name+strlen("fence_")); free(files[i]); files[i] = NULL; } free(files); sd->confignames[dircount] = NULL; } return (const char * const *)sd->confignames; } /* * Return STONITH info string */ static const char * fake_op(struct pluginDevice * sd, const char *op) { const char *pfx = "RHCS plugin "; char *ret = NULL; LOG(PIL_INFO, "rhcs plugins don't really support %s", op); ret = MALLOC(strlen(pfx) + strlen(op) + 1); strcpy(ret, pfx); strcat(ret, op); sd->outputbuf = ret; return(ret); } static const char * rhcs_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* sd; const char * op; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); sd = (struct pluginDevice *)s; if (sd->subplugin == NULL) { LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__); return(NULL); } if (!sd->metadata && !load_metadata(sd)) { return NULL; } switch (reqtype) { case ST_DEVICEID: op = "getinfo-devid"; return fake_op(sd, op); break; case ST_DEVICENAME: if (!proc_xpath("/resource-agent/shortdesc", sd, dump_content)) { return sd->outputbuf; } else { op = "getinfo-devname"; return fake_op(sd, op); } break; case ST_DEVICEDESCR: if (!proc_xpath("/resource-agent/longdesc", sd, dump_content)) { return sd->outputbuf; } else { op = "getinfo-devdescr"; return fake_op(sd, op); } break; case ST_DEVICEURL: op = "getinfo-devurl"; return fake_op(sd, op); break; case ST_CONF_XML: if (!proc_xpath("/resource-agent/parameters", sd, dump_params_xml)) { return sd->outputbuf; } break; default: return NULL; } return NULL; } /* * RHCS Stonith destructor... */ static void rhcs_destroy(StonithPlugin *s) { struct pluginDevice * sd; char ** p; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice *)s; sd->pluginid = NOTpluginID; rhcs_unconfig(sd); if (sd->confignames != NULL) { for (p = sd->confignames; *p; p++) { FREE(*p); } FREE(sd->confignames); sd->confignames = NULL; } if (sd->subplugin != NULL) { FREE(sd->subplugin); sd->subplugin = NULL; } if (sd->outputbuf != NULL) { FREE(sd->outputbuf); sd->outputbuf = NULL; } FREE(sd); } /* Create a new rhcs Stonith device */ static StonithPlugin * rhcs_new(const char *subplugin) { struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; if (subplugin != NULL) { sd->subplugin = STRDUP(subplugin); if (sd->subplugin == NULL) { FREE(sd); return(NULL); } } sd->sp.s_ops = &rhcsOps; return &(sd->sp); } #define MAXLINE 512 static void printparam_to_fd(int fd, const char *key, const char *value) { char arg[MAXLINE]; int cnt; cnt = snprintf(arg, MAXLINE, "%s=%s\n", key, value); if (cnt <= 0 || cnt >= MAXLINE) { LOG(PIL_CRIT, "%s: param/value pair too large", __FUNCTION__); return; } if (Debug) { LOG(PIL_DEBUG, "set rhcs plugin param '%s=%s'", key, value); } if (write(fd, arg, cnt) < 0) { LOG(PIL_CRIT, "%s: write: %m", __FUNCTION__); } } static void rhcs_print_var(gpointer key, gpointer value, gpointer user_data) { printparam_to_fd(GPOINTER_TO_UINT(user_data), (char *)key, (char *)value); } /* Run the command with op as command line argument(s) and return the exit * status + the output */ static int rhcs_run_cmd(struct pluginDevice *sd, const char *op, const char *host, char **output) { const int BUFF_LEN=4096; char buff[BUFF_LEN]; int read_len = 0; int rc; char * data = NULL; char cmd[FILENAME_MAX+64]; struct stat buf; int slen; int pid, status; int fd1[2]; /* our stdout/their stdin */ int fd2[2]; /* our stdin/their stdout and stderr */ rc = snprintf(cmd, FILENAME_MAX, "%s/fence_%s", STONITH_RHCS_PLUGINDIR, sd->subplugin); if (rc <= 0 || rc >= FILENAME_MAX) { LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__); return -1; } if (stat(cmd, &buf) != 0) { LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s", __FUNCTION__, cmd, strerror(errno)); return -1; } if (!S_ISREG(buf.st_mode) || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { LOG(PIL_CRIT, "%s: %s found NOT to be executable.", __FUNCTION__, cmd); return -1; } if (buf.st_mode & (S_IWGRP|S_IWOTH)) { LOG(PIL_CRIT, "%s: %s found to be writable by group/others, " "NOT executing for security purposes.", __FUNCTION__, cmd); return -1; } if (Debug) { LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd ); } if (pipe(fd1) || pipe(fd2)) goto err; pid = fork(); if (pid < 0) { LOG(PIL_CRIT, "%s: fork: %m", __FUNCTION__); goto err; } if (pid) { /* parent */ close(fd1[0]); close(fd2[1]); if (sd->cmd_opts) { printparam_to_fd(fd1[1], "agent", sd->subplugin); printparam_to_fd(fd1[1], "action", op); if( host ) printparam_to_fd(fd1[1], "nodename", host); g_hash_table_foreach(sd->cmd_opts, rhcs_print_var, GUINT_TO_POINTER(fd1[1])); } close(fd1[1]); /* we have nothing more to say */ fcntl(fd2[0], F_SETFL, fcntl(fd2[0], F_GETFL, 0) | O_NONBLOCK); data = NULL; slen=0; data = MALLOC(1); /* read stdout/stderr from the fence agent */ do { data[slen]=EOS; read_len = read(fd2[0], buff, BUFF_LEN); if (read_len > 0) { data=REALLOC(data, slen+read_len+1); if (data == NULL) { goto err; } memcpy(data+slen, buff, read_len); slen += read_len; data[slen] = EOS; } else if (read_len < 0) { if (errno == EAGAIN) continue; LOG(PIL_CRIT, "%s: read from pipe: %m", __FUNCTION__); goto err; } } while (read_len); if (!data) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); goto err; } close(fd2[0]); waitpid(pid, &status, 0); if (!WIFEXITED(status)) { LOG(PIL_CRIT, "%s: fence agent failed: %m", __FUNCTION__); goto err; } else { rc = WEXITSTATUS(status); if (rc) { LOG(PIL_CRIT, "%s: fence agent exit code: %d", __FUNCTION__, rc); goto err; } } } else { /* child */ close(fd1[1]); close(fd2[0]); close(STDIN_FILENO); if (dup(fd1[0]) < 0) goto err; close(fd1[0]); close(STDOUT_FILENO); if (dup(fd2[1]) < 0) goto err; close(STDERR_FILENO); if (dup(fd2[1]) < 0) goto err; close(fd2[1]); rc = sd->cmd_opts ? execlp(cmd, cmd, NULL) : execlp(cmd, cmd, "-o", op, NULL); if (rc < 0) { LOG(PIL_CRIT, "%s: Calling '%s' failed: %m", __FUNCTION__, cmd); } goto err; } if (Debug && data) { LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data); } if (output) { *output = data; } else { FREE(data); } return 0; err: if (data) { FREE(data); } if (output) { *output = NULL; } return(-1); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ribcl.py.in0000644000000000000000000000463712120057602027264 0ustar00usergroup00000000000000#!@PYTHON@ # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import sys import socket from httplib import * from time import sleep argv = sys.argv try: host = argv[1].split('.')[0]+'-rm' cmd = argv[2] except IndexError: print "Not enough arguments" sys.exit(1) login = [ '', '' ] logout = [ '', '' ] status = [ '', '', '' ] reset = [ '', '', '' ] off = [ '', '', '' ] on = [ '', '', '' ] todo = { 'reset':reset, 'on':on, 'off':off, 'status':status } acmds=[] try: if cmd == 'reset' and host.startswith('gfxcl'): acmds.append(login + todo['off'] + logout) acmds.append(login + todo['on'] + logout) else: acmds.append(login + todo[cmd] + logout) except KeyError: print "Invalid command: "+ cmd sys.exit(1) try: for cmds in acmds: c=HTTPSConnection(host) c.send('\r\n') c.sock.recv(1024) for line in cmds: c.send(line+'\r\n') c.sock.recv(1024) c.close() sleep(1) except socket.gaierror, msg: print msg sys.exit(1) except socket.sslerror, msg: print msg sys.exit(1) except socket.error, msg: print msg sys.exit(1) Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/riloe.c0000644000000000000000000001672712120057602026473 0ustar00usergroup00000000000000/* * Stonith module for RILOE Stonith device * * Copyright (c) 2004 Alain St-Denis * * Mangled by Zhaokai , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define DEVICE "Compaq RILOE" #include "stonith_plugin_common.h" #define PIL_PLUGIN riloe #define PIL_PLUGIN_S "riloe" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * riloe_new(const char *); static void riloe_destroy(StonithPlugin *); static int riloe_set_config(StonithPlugin *, StonithNVpair *); static const char * const * riloe_get_confignames(StonithPlugin * ); static const char * riloe_getinfo(StonithPlugin * s, int InfoType); static int riloe_status(StonithPlugin * ); static int riloe_reset_req(StonithPlugin * s, int request, const char * host); static char ** riloe_hostlist(StonithPlugin *); static struct stonith_ops riloeOps ={ riloe_new, /* Create new STONITH object */ riloe_destroy, /* Destroy STONITH object */ riloe_getinfo, /* Return STONITH info string */ riloe_get_confignames, /* Return STONITH info string */ riloe_set_config, /* Get configuration from NVpairs */ riloe_status, /* Return STONITH device status */ riloe_reset_req, /* Request a reset */ riloe_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &riloeOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } #define RILOE_COMMAND STONITH_MODULES "/ribcl.py" /* * Riloe STONITH device. We are very agreeable, but don't do much :-) */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char ** hostlist; int hostcount; }; static const char * pluginid = "RiloeDevice-Stonith"; static const char * NOTriloeID = "Riloe device has been destroyed"; #include "stonith_config_xml.h" static const char *riloeXML = XML_PARAMETERS_BEGIN XML_HOSTLIST_PARM XML_PARAMETERS_END; static int riloe_status(StonithPlugin *s) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); return S_OK; } /* * Return the list of hosts configured for this RILOE device */ static char ** riloe_hostlist(StonithPlugin *s) { struct pluginDevice* nd; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); nd = (struct pluginDevice*) s; if (nd->hostcount < 0) { LOG(PIL_CRIT , "unconfigured stonith object in %s", __FUNCTION__); return(NULL); } return OurImports->CopyHostList((const char * const*)nd->hostlist); } /* * Parse the config information, and stash it away... */ static int RILOE_parse_config_info(struct pluginDevice* nd, const char * info) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (nd->hostcount >= 0) { return(S_OOPS); } nd->hostlist = OurImports->StringToHostList(info); if (nd->hostlist == NULL) { LOG(PIL_CRIT,"StringToHostList() failed"); return S_OOPS; } for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) { g_strdown(nd->hostlist[nd->hostcount]); } return(S_OK); } /* * Pretend to reset the given host on this Stonith device. * (we don't even error check the "request" type) */ static int riloe_reset_req(StonithPlugin * s, int request, const char * host) { char cmd[4096]; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } snprintf(cmd, sizeof(cmd), "%s %s reset", RILOE_COMMAND, host); if (Debug) { LOG(PIL_DEBUG, "command %s will be executed", cmd); } if (system(cmd) == 0) { return S_OK; } else { LOG(PIL_CRIT, "command %s failed", cmd); return(S_RESETFAIL); } } /* * Parse the information in the given string, * and stash it away... */ static int riloe_set_config(StonithPlugin* s, StonithNVpair *list) { StonithNamesToGet namestocopy [] = { {ST_HOSTLIST, NULL} , {NULL, NULL} }; struct pluginDevice* nd; int rc; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); nd = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } rc = RILOE_parse_config_info(nd , namestocopy[0].s_value); FREE(namestocopy[0].s_value); return rc; } /* * Return the Stonith plugin configuration parameter */ static const char* const * riloe_get_confignames(StonithPlugin* p) { static const char * RiloeParams[] = {ST_HOSTLIST, NULL }; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } return RiloeParams; } /* * Return STONITH info string */ static const char * riloe_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* nd; const char * ret; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ nd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = nd->idinfo; break; case ST_DEVICEDESCR: ret = "Compaq RILOE STONITH device\n" "Very early version!"; break; case ST_DEVICEURL: ret = "http://www.hp.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = riloeXML; break; default: ret = NULL; break; } return ret; } /* * RILOE Stonith destructor... */ static void riloe_destroy(StonithPlugin *s) { struct pluginDevice* nd; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); nd = (struct pluginDevice *)s; nd->pluginid = NOTriloeID; if (nd->hostlist) { stonith_free_hostlist(nd->hostlist); nd->hostlist = NULL; } nd->hostcount = -1; FREE(nd); } /* Create a new Riloe Stonith device. Too bad this function can't be static */ static StonithPlugin * riloe_new(const char *subplugin) { struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (nd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(nd, 0, sizeof(*nd)); nd->pluginid = pluginid; nd->hostlist = NULL; nd->hostcount = -1; nd->idinfo = DEVICE; nd->sp.s_ops = &riloeOps; return &(nd->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/rps10.c0000644000000000000000000006432312120057602026321 0ustar00usergroup00000000000000/* * Stonith module for WTI Remote Power Controllers (RPS-10M device) * * Original code from baytech.c by * Copyright (c) 2000 Alan Robertson * * Modifications for WTI RPS10 * Copyright (c) 2000 Computer Generation Incorporated * Eric Z. Ayers * * Mangled by Zhaokai , IBM, 2005 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #define DEVICE "WTI RPS10 Power Switch" #include "stonith_plugin_common.h" #include #define PIL_PLUGIN rps10 #define PIL_PLUGIN_S "rps10" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #define ST_RPS10 "serial_to_targets" #define MAX_PRSID 256 #include static StonithPlugin * rps10_new(const char *); static void rps10_destroy(StonithPlugin *); static int rps10_set_config(StonithPlugin *, StonithNVpair *); static const char * const * rps10_get_confignames(StonithPlugin *); static const char * rps10_getinfo(StonithPlugin * s, int InfoType); static int rps10_status(StonithPlugin * ); static int rps10_reset_req(StonithPlugin * s, int request, const char * host); static char ** rps10_hostlist(StonithPlugin *); static struct stonith_ops rps10Ops ={ rps10_new, /* Create new STONITH object */ rps10_destroy, /* Destroy STONITH object */ rps10_getinfo, /* Return STONITH info string */ rps10_get_confignames, /* Return STONITH info string */ rps10_set_config, /* Get configuration from NVpairs */ rps10_status, /* Return STONITH device status */ rps10_reset_req, /* Request a reset */ rps10_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_signal.h" #define DOESNT_USE_STONITHKILLCOMM #define DOESNT_USE_STONITHSCANLINE #include "stonith_expect_helpers.h" PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &rps10Ops , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * This was written for a Western Telematic Inc. (WTI) * Remote Power Switch - RPS-10M. * * It has a DB9 serial port, a Rotary Address Switch, * and a pair of RJ-11 jacks for linking multiple switches * together. The 'M' unit is a master unit which can control * up to 9 additional slave units. (the master unit also has an * A/C outlet, so you can control up to 10 devices) * * There are a set of dip switches. The default shipping configuration * is with all dip switches down. I highly recommend that you flip * switch #3 up, so that when the device is plugged in, the power * to the unit comes on. * * The serial interface is fixed at 9600 BPS (well, you *CAN* * select 2400 BPS with a dip switch, but why?) 8-N-1 * * The ASCII command string is: * * ^B^X^X^B^X^Xac^M * * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... ) * ^M the carriage return character * * a = 0-9 Indicates the address of the module to receive the command * a = * Sends the command to all modules * * c = 0 Switch the AC outlet OFF * Returns: * Plug 0 Off * Complete * * c = 1 Switch the AC outlet ON * Returns: * Plug 0 On * Complete * * c = T Toggle AC OFF (delay) then back ON * Returns: * Plug 0 Off * Plug 0 On * Complete * * c = ? Read and display status of the selected module * Returns: * Plug 0 On # or Plug 0 Off * Complete * * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON * * 21 September 2000 * Eric Z. Ayers * Computer Generation, Inc. */ struct cntrlr_str { char outlet_id; /* value 0-9, '*' */ char * node; /* name of the node attached to this outlet */ }; struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; int fd; /* FD open to the serial port */ char * device; /* Serial device name to use to communicate to this RPS10 */ #define WTI_NUM_CONTROLLERS 10 struct cntrlr_str controllers[WTI_NUM_CONTROLLERS]; /* one master switch can address 10 controllers */ /* Number of actually configured units */ int unit_count; }; /* This string is used to identify this type of object in the config file */ static const char * pluginid = "WTI_RPS10"; static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)"; #include "stonith_config_xml.h" #define XML_RPS10_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "Value in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \ XML_PARM_SHORTDESC_END #define XML_RPS10_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The RPS-10 STONITH device configuration information in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \ XML_PARM_LONGDESC_END #define XML_RPS10_PARM \ XML_PARAMETER_BEGIN(ST_RPS10, "string", "1") \ XML_RPS10_SHORTDESC \ XML_RPS10_LONGDESC \ XML_PARAMETER_END static const char *rps10XML = XML_PARAMETERS_BEGIN XML_RPS10_PARM XML_PARAMETERS_END; /* WTIpassword - The fixed string ^B^X^X^B^X^X */ static const char WTIpassword[7] = {2,24,24,2,24,24,0}; /* * Different expect strings that we get from the WTI_RPS10 * Remote Power Controllers... */ static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}}; static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}}; static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}}; static struct Etoken WTItokOutlet[] = { {"0", 0, 0}, {"1", 0, 0}, {"2", 0, 0}, {"3", 0, 0}, {"4", 0, 0}, {"5", 0, 0}, {"6", 0, 0}, {"7", 0, 0}, {"8", 0, 0}, {"9", 0, 0}, {NULL,0,0}}; static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}}; /* * Tokens currently not used because they don't show up on all RPS10 units: * */ static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}}; /* Accept either a CR/NL or an NL/CR */ static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}}; static int RPSConnect(struct pluginDevice * ctx); static int RPSDisconnect(struct pluginDevice * ctx); static int RPSReset(struct pluginDevice*, char unit_id, const char * rebootid); #if defined(ST_POWERON) static int RPSOn(struct pluginDevice*, char unit_id, const char * rebootid); #endif #if defined(ST_POWEROFF) static int RPSOff(struct pluginDevice*, char unit_id, const char * rebootid); #endif static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host ); static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info); #define SENDCMD(outlet, cmd, timeout) { \ int ret_val = RPSSendCommand(ctx, outlet, cmd, timeout);\ if (ret_val != S_OK) { \ return ret_val; \ } \ } /* * RPSSendCommand - send a command to the specified outlet */ static int RPSSendCommand (struct pluginDevice *ctx, char outlet, char command, int timeout) { char writebuf[10]; /* all commands are 9 chars long! */ int return_val; /* system call result */ fd_set rfds, wfds, xfds; struct timeval tv; /* */ /* list of FDs for select() */ if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); snprintf (writebuf, sizeof(writebuf), "%s%c%c\r", WTIpassword, outlet, command); if (Debug) { LOG(PIL_DEBUG, "Sending %s\n", writebuf); } /* Make sure the serial port won't block on us. use select() */ FD_SET(ctx->fd, &wfds); FD_SET(ctx->fd, &xfds); tv.tv_sec = timeout; tv.tv_usec = 0; return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv); if (return_val == 0) { /* timeout waiting on serial port */ LOG(PIL_CRIT, "%s: Timeout writing to %s", pluginid, ctx->device); return S_TIMEOUT; } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) { /* an error occured */ LOG(PIL_CRIT, "%s: Error before writing to %s: %s", pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* send the command */ if (write(ctx->fd, writebuf, strlen(writebuf)) != (int)strlen(writebuf)) { LOG(PIL_CRIT, "%s: Error writing to %s : %s", pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* suceeded! */ return S_OK; } /* end RPSSendCommand() */ /* * RPSReset - Reset (power-cycle) the given outlet id */ static int RPSReset(struct pluginDevice* ctx, char unit_id, const char * rebootid) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, ctx->device); return S_OOPS; } /* send the "toggle power" command */ SENDCMD(unit_id, 'T', 10); /* Expect "Plug 0 Off" */ /* Note: If asked to control "*", the RPS10 will report all units it * separately; however, we don't know how many, so we can only wait * for the first unit to report something and then wait until the * "Complete" */ EXPECT(ctx->fd, WTItokPlug, 5); if (Debug) { LOG(PIL_DEBUG, "Got Plug\n"); } EXPECT(ctx->fd, WTItokOutlet, 2); if (Debug) { LOG(PIL_DEBUG, "Got Outlet #\n"); } EXPECT(ctx->fd, WTItokOff, 2); if (Debug) { LOG(PIL_DEBUG, "Got Off\n"); } EXPECT(ctx->fd, WTItokCRNL, 2); LOG(PIL_INFO, "Host is being rebooted: %s", rebootid); /* Expect "Complete" */ EXPECT(ctx->fd, WTItokComplete, 14); if (Debug) { LOG(PIL_DEBUG, "Got Complete\n"); } EXPECT(ctx->fd, WTItokCRNL, 2); if (Debug) { LOG(PIL_DEBUG, "Got NL\n"); } return(S_OK); } /* end RPSReset() */ #if defined(ST_POWERON) /* * RPSOn - Turn OFF the given outlet id */ static int RPSOn(struct pluginDevice* ctx, char unit_id, const char * host) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, ctx->device); return S_OOPS; } /* send the "On" command */ SENDCMD(unit_id, '1', 10); /* Expect "Plug 0 On" */ EXPECT(ctx->fd, WTItokPlug, 5); EXPECT(ctx->fd, WTItokOutlet, 2); EXPECT(ctx->fd, WTItokOn, 2); EXPECT(ctx->fd, WTItokCRNL, 2); LOG(PIL_INFO, "Host is being turned on: %s", host); /* Expect "Complete" */ EXPECT(ctx->fd, WTItokComplete, 5); EXPECT(ctx->fd, WTItokCRNL, 2); return(S_OK); } /* end RPSOn() */ #endif #if defined(ST_POWEROFF) /* * RPSOff - Turn Off the given outlet id */ static int RPSOff(struct pluginDevice* ctx, char unit_id, const char * host) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (ctx->fd < 0) { LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, ctx->device); return S_OOPS; } /* send the "Off" command */ SENDCMD(unit_id, '0', 10); /* Expect "Plug 0 Off" */ EXPECT(ctx->fd, WTItokPlug, 5); EXPECT(ctx->fd, WTItokOutlet, 2); EXPECT(ctx->fd, WTItokOff, 2); EXPECT(ctx->fd, WTItokCRNL, 2); LOG(PIL_INFO, "Host is being turned on: %s", host); /* Expect "Complete" */ EXPECT(ctx->fd, WTItokComplete, 5); EXPECT(ctx->fd, WTItokCRNL, 2); return(S_OK); } /* end RPSOff() */ #endif /* * rps10_status - API entry point to probe the status of the stonith device * (basically just "is it reachable and functional?", not the * status of the individual outlets) * * Returns: * S_OOPS - some error occured * S_OK - if the stonith device is reachable and online. */ static int rps10_status(StonithPlugin *s) { struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); ctx = (struct pluginDevice*) s; if (RPSConnect(ctx) != S_OK) { return(S_OOPS); } /* The "connect" really does enough work to see if the controller is alive... It verifies that it is returning RPS-10 Ready */ return(RPSDisconnect(ctx)); } /* * rps10_hostlist - API entry point to return the list of hosts * for the devices on this WTI_RPS10 unit * * This type of device is configured from the config file, * so we don't actually have to connect to figure this * out, just peruse the 'ctx' structure. * Returns: * NULL on error * a malloced array, terminated with a NULL, * of null-terminated malloc'ed strings. */ static char ** rps10_hostlist(StonithPlugin *s) { char ** ret = NULL; /* list to return */ int i; int j; struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,NULL); ctx = (struct pluginDevice*) s; if (ctx->unit_count >= 1) { ret = (char **)MALLOC((ctx->unit_count+1)*sizeof(char*)); if (ret == NULL) { LOG(PIL_CRIT, "out of memory"); return ret; } ret[ctx->unit_count]=NULL; /* null terminate the array */ for (i=0; i < ctx->unit_count; i++) { ret[i] = STRDUP(ctx->controllers[i].node); if (ret[i] == NULL) { for(j=0; j for this module is: * [ ] ... * * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through * a device attached to serial port /dev/ttyS0. * A machine named 'nodeb' can kill machines 'nodea' and 'nodec' * through a device attached to serial port /dev/ttyS1 (outlets 0 * and 1 respectively) * * * * stonith nodea rps10 /dev/ttyS0 nodeb 0 * stonith nodeb rps10 /dev/ttyS0 nodea 0 nodec 1 * * Another possible configuration is for 2 stonith devices * accessible through 2 different serial ports on nodeb: * * stonith nodeb rps10 /dev/ttyS0 nodea 0 * stonith nodeb rps10 /dev/ttyS1 nodec 0 */ /* * OOPS! * * Most of the large block of comments above is incorrect as far as this * module is concerned. It is somewhat applicable to the heartbeat code, * but not to this Stonith module. * * The format of parameter string for this module is: * [ ] ... */ static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info) { char *copy; char *token; char *outlet, *node; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* strtok() is nice to use to parse a string with (other than it isn't threadsafe), but it is destructive, so we're going to alloc our own private little copy for the duration of this function. */ copy = STRDUP(info); if (!copy) { LOG(PIL_CRIT, "out of memory"); return S_OOPS; } /* Grab the serial device */ token = strtok (copy, " \t"); if (!token) { LOG(PIL_CRIT, "%s: Can't find serial device on config line '%s'", pluginid, info); goto token_error; } ctx->device = STRDUP(token); if (!ctx->device) { LOG(PIL_CRIT, "out of memory"); goto token_error; } /* Loop through the rest of the command line which should consist of */ /* pairs */ while ((node = strtok (NULL, " \t")) && (outlet = strtok (NULL, " \t\n"))) { char outlet_id; /* validate the outlet token */ if ((sscanf (outlet, "%c", &outlet_id) != 1) || !( ((outlet_id >= '0') && (outlet_id <= '9')) || (outlet_id == '*') || (outlet_id == 'A') ) ) { LOG(PIL_CRIT , "%s: the outlet_id %s must be between" " 0 and 9 or '*' / 'A'", pluginid, outlet); goto token_error; } if (outlet_id == 'A') { /* Remap 'A' to '*'; in some configurations, * a '*' can't be configured because it breaks * scripts -- lmb */ outlet_id = '*'; } if (ctx->unit_count >= WTI_NUM_CONTROLLERS) { LOG(PIL_CRIT, "%s: Tried to configure too many controllers", pluginid); goto token_error; } ctx->controllers[ctx->unit_count].node = STRDUP(node); g_strdown(ctx->controllers[ctx->unit_count].node); ctx->controllers[ctx->unit_count].outlet_id = outlet_id; ctx->unit_count++; } /* free our private copy of the string we've been destructively * parsing with strtok() */ FREE(copy); return ((ctx->unit_count > 0) ? S_OK : S_BADCONFIG); token_error: FREE(copy); if (ctx->device) { FREE(ctx->device); ctx->device = NULL; } return(S_BADCONFIG); } /* * dtrtoggle - toggle DTR on the serial port * * snarfed from minicom, sysdep1.c, a well known POSIX trick. * */ static void dtrtoggle(int fd) { struct termios tty, old; int sec = 2; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } tcgetattr(fd, &tty); tcgetattr(fd, &old); cfsetospeed(&tty, B0); cfsetispeed(&tty, B0); tcsetattr(fd, TCSANOW, &tty); if (sec>0) { sleep(sec); tcsetattr(fd, TCSANOW, &old); } if (Debug) { LOG(PIL_DEBUG, "dtrtoggle Complete (%s)\n", pluginid); } } /* * RPSConnect - * * Connect to the given WTI_RPS10 device. * Side Effects * DTR on the serial port is toggled * ctx->fd now contains a valid file descriptor to the serial port * ??? LOCK THE SERIAL PORT ??? * * Returns * S_OK on success * S_OOPS on error * S_TIMEOUT if the device did not respond * */ static int RPSConnect(struct pluginDevice * ctx) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* Open the serial port if it isn't already open */ if (ctx->fd < 0) { struct termios tio; if (OurImports->TtyLock(ctx->device) < 0) { LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid); return S_OOPS; } ctx->fd = open (ctx->device, O_RDWR); if (ctx->fd <0) { LOG(PIL_CRIT, "%s: Can't open %s : %s", pluginid, ctx->device, strerror(errno)); return S_OOPS; } /* set the baudrate to 9600 8 - N - 1 */ memset (&tio, 0, sizeof(tio)); /* ??? ALAN - the -tradtitional flag on gcc causes the CRTSCTS constant to generate a warning, and warnings are treated as errors, so I can't set this flag! - EZA ??? Hmmm. now that I look at the documentation, RTS is just wired high on this device! we don't need it. */ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ; tio.c_lflag = ICANON; if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) { LOG(PIL_CRIT, "%s: Can't set attributes %s : %s", pluginid, ctx->device, strerror(errno)); close (ctx->fd); OurImports->TtyUnlock(ctx->device); ctx->fd=-1; return S_OOPS; } /* flush all data to and fro the serial port before we start */ if (tcflush (ctx->fd, TCIOFLUSH) < 0) { LOG(PIL_CRIT, "%s: Can't flush %s : %s", pluginid, ctx->device, strerror(errno)); close (ctx->fd); OurImports->TtyUnlock(ctx->device); ctx->fd=-1; return S_OOPS; } } /* Toggle DTR - this 'resets' the controller serial port interface In minicom, try CTRL-A H to hangup and you can see this behavior. */ dtrtoggle(ctx->fd); /* Wait for the switch to respond with "RPS-10 Ready". Emperically, this usually takes 5-10 seconds... ... If this fails, this may be a hint that you got a broken serial cable, which doesn't connect hardware flow control. */ if (Debug) { LOG(PIL_DEBUG, "Waiting for READY\n"); } EXPECT(ctx->fd, WTItokReady, 12); if (Debug) { LOG(PIL_DEBUG, "Got READY\n"); } EXPECT(ctx->fd, WTItokCRNL, 2); if (Debug) { LOG(PIL_DEBUG, "Got NL\n"); } return(S_OK); } static int RPSDisconnect(struct pluginDevice * ctx) { if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (ctx->fd >= 0) { /* Flush the serial port, we don't care what happens to the * characters and failing to do this can cause close to hang. */ tcflush(ctx->fd, TCIOFLUSH); close (ctx->fd); if (ctx->device != NULL) { OurImports->TtyUnlock(ctx->device); } } ctx->fd = -1; return S_OK; } /* * RPSNametoOutlet - Map a hostname to an outlet on this stonith device. * * Returns: * 0-9, * on success ( the outlet id on the RPS10 ) * -1 on failure (host not found in the config file) * */ static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host ) { int i=0; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* scan the controllers[] array to see if this host is there */ for (i=0;iunit_count;i++) { /* return the outlet id */ if ( ctx->controllers[i].node && !strcasecmp(host, ctx->controllers[i].node)) { /* found it! */ break; } } if (i == ctx->unit_count) { return -1; } else { return ctx->controllers[i].outlet_id; } } /* * rps10_reset - API call to Reset (reboot) the given host on * this Stonith device. This involves toggling the power off * and then on again, OR just calling the builtin reset command * on the stonith device. */ static int rps10_reset_req(StonithPlugin * s, int request, const char * host) { int rc = S_OK; int lorc = S_OK; signed char outlet_id = -1; struct pluginDevice* ctx; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); ctx = (struct pluginDevice*) s; if ((rc = RPSConnect(ctx)) != S_OK) { return(rc); } outlet_id = RPSNametoOutlet(ctx, host); if (outlet_id < 0) { LOG(PIL_WARN, "%s: %s doesn't control host [%s]" , pluginid, ctx->device, host ); RPSDisconnect(ctx); return(S_BADHOST); } switch(request) { #if defined(ST_POWERON) case ST_POWERON: rc = RPSOn(ctx, outlet_id, host); break; #endif #if defined(ST_POWEROFF) case ST_POWEROFF: rc = RPSOff(ctx, outlet_id, host); break; #endif case ST_GENERIC_RESET: rc = RPSReset(ctx, outlet_id, host); break; default: rc = S_INVAL; break; } lorc = RPSDisconnect(ctx); return(rc != S_OK ? rc : lorc); } /* * Parse the information in the given string, * and stash it away... */ static int rps10_set_config(StonithPlugin* s, StonithNVpair* list) { struct pluginDevice* ctx; StonithNamesToGet namestocopy [] = { {ST_RPS10, NULL} , {NULL, NULL} }; int rc=0; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); if (s->isconfigured) { /* The module is already configured. */ return(S_OOPS); } ctx = (struct pluginDevice*) s; if((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK){ LOG(PIL_DEBUG , "get all calues failed"); return rc; } rc = RPS_parse_config_info(ctx, namestocopy[0].s_value); FREE(namestocopy[0].s_value); return rc; } /* * Return the Stonith plugin configuration parameter * */ static const char * const * rps10_get_confignames(StonithPlugin* p) { static const char * Rps10Params[] = {ST_RPS10 ,NULL }; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } return Rps10Params; } /* * rps10_getinfo - API entry point to retrieve something from the handle */ static const char * rps10_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice* ctx; const char * ret; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ ctx = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = ctx->idinfo; break; case ST_DEVICENAME: ret = ctx->device; break; case ST_DEVICEDESCR: ret = "Western Telematic Inc. (WTI) " "Remote Power Switch - RPS-10M.\n"; break; case ST_DEVICEURL: ret = "http://www.wti.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = rps10XML; break; default: ret = NULL; break; } return ret; } /* * rps10_destroy - API entry point to destroy a WTI_RPS10 Stonith object. */ static void rps10_destroy(StonithPlugin *s) { struct pluginDevice* ctx; int i; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); ctx = (struct pluginDevice *)s; ctx->pluginid = NOTwtiid; /* close the fd if open and set ctx->fd to invalid */ RPSDisconnect(ctx); if (ctx->device != NULL) { FREE(ctx->device); ctx->device = NULL; } if (ctx->unit_count > 0) { for (i = 0; i < ctx->unit_count; i++) { if (ctx->controllers[i].node != NULL) { FREE(ctx->controllers[i].node); ctx->controllers[i].node = NULL; } } } FREE(ctx); } /* * rps10_new - API entry point called to create a new WTI_RPS10 Stonith device * object. */ static StonithPlugin * rps10_new(const char *subplugin) { struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if (ctx == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(ctx, 0, sizeof(*ctx)); ctx->pluginid = pluginid; ctx->fd = -1; ctx->unit_count = 0; ctx->device = NULL; ctx->idinfo = DEVICE; ctx->sp.s_ops = &rps10Ops; return &(ctx->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/ssh.c0000644000000000000000000002107512120057602026146 0ustar00usergroup00000000000000/* * Stonith module for SSH Stonith device * * Copyright (c) 2001 SuSE Linux AG * * Authors: Joachim Gleissner , Lars Marowsky-Brée * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #define DEVICE "SSH STONITH device" #include "stonith_plugin_common.h" #define PIL_PLUGIN ssh #define PIL_PLUGIN_S "ssh" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * ssh_new(const char *); static void ssh_destroy(StonithPlugin *); static const char * const * ssh_get_confignames(StonithPlugin *); static int ssh_set_config(StonithPlugin *, StonithNVpair*); static const char * ssh_get_info(StonithPlugin * s, int InfoType); static int ssh_status(StonithPlugin * ); static int ssh_reset_req(StonithPlugin * s, int request , const char * host); static char ** ssh_hostlist(StonithPlugin *); static struct stonith_ops sshOps ={ ssh_new, /* Create new STONITH object */ ssh_destroy, /* Destroy STONITH object */ ssh_get_info, /* Return STONITH info string */ ssh_get_confignames, /* Return configuration parameters */ ssh_set_config, /* set configuration */ ssh_status, /* Return STONITH device status */ ssh_reset_req, /* Request a reset */ ssh_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &sshOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* uncomment this if you have an ssh that can do what it claims #define SSH_COMMAND "ssh -q -x -o PasswordAuthentication=no StrictHostKeyChecking=no" */ /* use this if you have the (broken) OpenSSH 2.1.1 */ /* sunjd@cn.ibm.com added the option -f to temporily work around the block issue * in which the child process always stay in 'system' call. Please FIX this. * Additonally, this issue seems related to both of 2.6 kernel and stonithd. */ #define SSH_COMMAND "ssh -q -x -n -l root" /* We need to do a real hard reboot without syncing anything to simulate a * power cut. * We have to do it in the background, otherwise this command will not * return. */ #define REBOOT_COMMAND "nohup sh -c '(sleep 2; nohup " REBOOT " " REBOOT_OPTIONS ") /dev/null 2>&1' &" #undef REBOOT_COMMAND #define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1" #define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1" #define MAX_PING_ATTEMPTS 15 /* * SSH STONITH device * * I used the null device as template, so I guess there is missing * some functionality. * */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; char ** hostlist; int hostcount; }; static const char * pluginid = "SSHDevice-Stonith"; static const char * NOTpluginid = "SSH device has been destroyed"; #include "stonith_config_xml.h" static const char *sshXML = XML_PARAMETERS_BEGIN XML_HOSTLIST_PARM XML_PARAMETERS_END; static int ssh_status(StonithPlugin *s) { ERRIFWRONGDEV(s, S_OOPS); return system(NULL) ? S_OK : S_OOPS; } /* * Return the list of hosts configured for this SSH device */ static char ** ssh_hostlist(StonithPlugin *s) { struct pluginDevice* sd = (struct pluginDevice*)s; ERRIFWRONGDEV(s, NULL); if (sd->hostcount < 0) { LOG(PIL_CRIT , "unconfigured stonith object in %s", __FUNCTION__); return(NULL); } return OurImports->CopyHostList((const char * const *)sd->hostlist); } /* * Reset the given host on this Stonith device. */ static int ssh_reset_req(StonithPlugin * s, int request, const char * host) { struct pluginDevice* sd = (struct pluginDevice *)s; char cmd[4096]; int i, status = -1; ERRIFWRONGDEV(s, S_OOPS); if (request == ST_POWERON) { LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE); return S_INVAL; } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) { return S_INVAL; } for (i = 0; i < sd->hostcount; i++) { if (strcasecmp(host, sd->hostlist[i]) == 0) { break; } } if (i >= sd->hostcount) { LOG(PIL_CRIT, "%s doesn't control host [%s]" , sd->idinfo, host); return(S_BADHOST); } LOG(PIL_INFO, "Initiating ssh-%s on host: %s" , request == ST_POWEROFF ? "poweroff" : "reset", host); snprintf(cmd, sizeof(cmd)-1, "%s \"%s\" \"%s\"", SSH_COMMAND , host , request == ST_POWEROFF ? POWEROFF_COMMAND : REBOOT_COMMAND); status = system(cmd); if (WIFEXITED(status) && 0 == WEXITSTATUS(status)) { if (Debug) { LOG(PIL_DEBUG, "checking whether %s stonith'd", host); } snprintf(cmd, sizeof(cmd)-1 , "ping -w1 -c1 %s >/dev/null 2>&1", host); for (i = 0; i < MAX_PING_ATTEMPTS; i++) { status = system(cmd); if (WIFEXITED(status) && 1 == WEXITSTATUS(status)) { if (Debug) { LOG(PIL_DEBUG, "unable to ping %s" " after %d tries, stonith did work" , host, i); } return S_OK; } sleep(1); } LOG(PIL_CRIT, "still able to ping %s after %d tries, stonith" " did not work", host, MAX_PING_ATTEMPTS); return S_RESETFAIL; }else{ LOG(PIL_CRIT, "command %s failed", cmd); return S_RESETFAIL; } } static const char * const * ssh_get_confignames(StonithPlugin* p) { static const char * SshParams[] = {ST_HOSTLIST, NULL }; return SshParams; } /* * Parse the config information in the given string, and stash it away... */ static int ssh_set_config(StonithPlugin* s, StonithNVpair* list) { struct pluginDevice* sd = (struct pluginDevice *)s; const char * hlist; ERRIFWRONGDEV(s,S_OOPS); if ((hlist = OurImports->GetValue(list, ST_HOSTLIST)) == NULL) { return S_OOPS; } sd->hostlist = OurImports->StringToHostList(hlist); if (sd->hostlist == NULL) { LOG(PIL_CRIT, "out of memory"); sd->hostcount = 0; }else{ for (sd->hostcount = 0; sd->hostlist[sd->hostcount] ; sd->hostcount++) { g_strdown(sd->hostlist[sd->hostcount]); } } return sd->hostcount ? S_OK : S_OOPS; } static const char * ssh_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice* sd = (struct pluginDevice *)s; const char * ret; ERRIFWRONGDEV(s, NULL); switch (reqtype) { case ST_DEVICEID: ret = sd->idinfo; break; case ST_DEVICENAME: ret = "ssh STONITH device"; break; case ST_DEVICEDESCR: /* Description of device type */ ret = "SSH-based host reset\n" "Fine for testing, but not suitable for production!"; break; case ST_DEVICEURL: ret = "http://openssh.org"; break; case ST_CONF_XML: /* XML metadata */ ret = sshXML; break; default: ret = NULL; break; } return ret; } /* * SSH Stonith destructor... */ static void ssh_destroy(StonithPlugin *s) { struct pluginDevice* sd = (struct pluginDevice *)s; VOIDERRIFWRONGDEV(s); sd->pluginid = NOTpluginid; if (sd->hostlist) { stonith_free_hostlist(sd->hostlist); sd->hostlist = NULL; } sd->hostcount = -1; FREE(sd); } /* Create a new ssh Stonith device */ static StonithPlugin* ssh_new(const char *subplugin) { struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice); if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; sd->hostlist = NULL; sd->hostcount = -1; sd->idinfo = DEVICE; sd->sp.s_ops = &sshOps; return &(sd->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/stonith_config_xml.h0000644000000000000000000001106412120057602031250 0ustar00usergroup00000000000000/* * stonith_config_xml.h: common macros easing the writing of config * XML for STONITH plugins. Only a STONITH * plugin should include this header! * * Copyright (C) International Business Machines Corp., 2005 * Author: Dave Blaschke * Support: linux-ha@lists.linux-ha.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _STONITH_CONFIG_XML_H #define _STONITH_CONFIG_XML_H /* * The generic constants for XML */ /* ? */ #define XML_PARAMETERS_BEGIN "" #define XML_PARAMETERS_END "" /* ? */ #define XML_PARAMETER_BEGIN(name,type,req) \ "" \ "\n" #define XML_PARAMETER_END "\n" /* ? */ #define XML_PARM_SHORTDESC_BEGIN(lang) \ "\n" #define XML_PARM_SHORTDESC_END "\n" /* ? */ #define XML_PARM_LONGDESC_BEGIN(lang) \ "\n" #define XML_PARM_LONGDESC_END "\n" /* * The short and long descriptions for the few standardized parameter names; * these can be translated by appending different languages to these constants * (must include XML_PARM_****DESC_BEGIN(), the translated description, and * XML_PARM_****DESC_END for each language) */ #define XML_HOSTLIST_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "Hostlist" \ XML_PARM_SHORTDESC_END #define XML_HOSTLIST_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The list of hosts that the STONITH device controls" \ XML_PARM_LONGDESC_END #define XML_IPADDR_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "IP Address" \ XML_PARM_SHORTDESC_END #define XML_IPADDR_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The IP address of the STONITH device" \ XML_PARM_LONGDESC_END #define XML_LOGIN_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "Login" \ XML_PARM_SHORTDESC_END #define XML_LOGIN_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The username used for logging in to the STONITH device" \ XML_PARM_LONGDESC_END #define XML_PASSWD_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "Password" \ XML_PARM_SHORTDESC_END #define XML_PASSWD_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The password used for logging in to the STONITH device" \ XML_PARM_LONGDESC_END #define XML_COMMUNITY_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "SNMP Community" \ XML_PARM_SHORTDESC_END #define XML_COMMUNITY_LONGDESC "" \ XML_PARM_LONGDESC_BEGIN("en") \ "The SNMP community string associated with the STONITH device" \ XML_PARM_LONGDESC_END #define XML_TTYDEV_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ "TTY Device" \ XML_PARM_SHORTDESC_END #define XML_TTYDEV_LONGDESC "" \ XML_PARM_LONGDESC_BEGIN("en") \ "The TTY device used for connecting to the STONITH device" \ XML_PARM_LONGDESC_END /* * Complete parameter descriptions for the few standardized parameter names */ #define XML_HOSTLIST_PARM \ XML_PARAMETER_BEGIN(ST_HOSTLIST, "string", "1") \ XML_HOSTLIST_SHORTDESC \ XML_HOSTLIST_LONGDESC \ XML_PARAMETER_END #define XML_IPADDR_PARM \ XML_PARAMETER_BEGIN(ST_IPADDR, "string", "1") \ XML_IPADDR_SHORTDESC \ XML_IPADDR_LONGDESC \ XML_PARAMETER_END #define XML_LOGIN_PARM \ XML_PARAMETER_BEGIN(ST_LOGIN, "string", "1") \ XML_LOGIN_SHORTDESC \ XML_LOGIN_LONGDESC \ XML_PARAMETER_END #define XML_PASSWD_PARM \ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "1") \ XML_PASSWD_SHORTDESC \ XML_PASSWD_LONGDESC \ XML_PARAMETER_END #define XML_COMMUNITY_PARM \ XML_PARAMETER_BEGIN(ST_COMMUNITY, "string", "1") \ XML_COMMUNITY_SHORTDESC \ XML_COMMUNITY_LONGDESC \ XML_PARAMETER_END #define XML_TTYDEV_PARM \ XML_PARAMETER_BEGIN(ST_TTYDEV, "string", "1") \ XML_TTYDEV_SHORTDESC \ XML_TTYDEV_LONGDESC \ XML_PARAMETER_END #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/stonith_expect_helpers.h0000644000000000000000000000614612120057602032142 0ustar00usergroup00000000000000/* * stonith_expect_helpers.h: Some common expect defines. * * Copyright (C) 2004 Lars Marowsky-Bree * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* This is still somewhat ugly. It needs to be included after the PILS * definitions so that it can access them, but the code reduction seemed * to justify this. Hopefully it can be made somewhat more elegant * eventually. */ /* * Many expect/telnet plugins use these defines and functions. */ #define SEND(fd,s) { \ size_t slen = strlen(s); \ if (Debug) { \ LOG(PIL_DEBUG \ , "Sending [%s] (len %d)" \ , (s) \ , (int)slen); \ } \ if (write((fd), (s), slen) != slen) { \ LOG(PIL_CRIT \ , "%s: write failed" \ , __FUNCTION__); \ } \ } #define EXPECT(fd,p,t) { \ if (StonithLookFor(fd, p, t) < 0) \ return(errno == ETIMEDOUT \ ? S_TIMEOUT : S_OOPS); \ } #define NULLEXPECT(fd,p,t) { \ if (StonithLookFor(fd, p, t) < 0) \ return(NULL); \ } #define SNARF(fd,s, to) { \ if (StonithScanLine(fd,to,(s),sizeof(s))\ != S_OK){ \ return(S_OOPS); \ } \ } #define NULLSNARF(fd,s, to){ \ if (StonithScanLine(fd,to,(s),sizeof(s))\ != S_OK) { \ return(NULL); \ } \ } /* Look for any of the given patterns. We don't care which */ static int StonithLookFor(int fd, struct Etoken * tlist, int timeout) { int rc; char savebuf[512]; if ((rc = EXPECT_TOK(fd, tlist, timeout, savebuf, sizeof(savebuf) , Debug)) < 0) { LOG(PIL_CRIT, "Did not find string %s from " DEVICE "." , tlist[0].string); LOG(PIL_CRIT, "Received [%s]", savebuf); } return(rc); } #ifndef DOESNT_USE_STONITHSCANLINE /* Accept either a CR/NL or an NL/CR */ static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}}; static int StonithScanLine(int fd, int timeout, char * buf, int max) { if (EXPECT_TOK(fd, CRNL, timeout, buf, max, Debug) < 0) { LOG(PIL_CRIT, "Could not read line from" DEVICE "."); return(S_OOPS); } return(S_OK); } #endif #ifndef DOESNT_USE_STONITHKILLCOMM static void Stonithkillcomm(int *rdfd, int *wrfd, int *pid) { if ((rdfd != NULL) && (*rdfd >= 0)) { close(*rdfd); *rdfd = -1; } if ((wrfd != NULL) && (*wrfd >= 0)) { close(*wrfd); *wrfd = -1; } if ((pid != NULL) && (*pid > 0)) { STONITH_KILL(*pid, SIGKILL); (void)waitpid(*pid, NULL, 0); *pid = -1; } } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/stonith_plugin_common.h0000644000000000000000000000630712120057602031775 0ustar00usergroup00000000000000/* * stonith_plugin_common.h: common macros easing the writing of STONITH * plugins. Only a STONITH plugin should * include this header! * * Copyright (C) 2004 Lars Marowsky-Bree * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _STONITH_PLUGIN_COMMON_H #define _STONITH_PLUGIN_COMMON_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_TERMIO_H # include #endif #ifdef HAVE_SYS_TERMIOS_H #include #else #ifdef HAVE_TERMIOS_H #include #endif #endif #include #include #include #define LOG(w...) PILCallLog(PluginImports->log, w) #define MALLOC PluginImports->alloc #define REALLOC PluginImports->mrealloc #define STRDUP PluginImports->mstrdup #define FREE PluginImports->mfree #define EXPECT_TOK OurImports->ExpectToken #define STARTPROC OurImports->StartProcess #ifdef MALLOCT # undef MALLOCT #endif #define ST_MALLOCT(t) ((t *)(MALLOC(sizeof(t)))) #define N_(text) (text) #define _(text) dgettext(ST_TEXTDOMAIN, text) #define WHITESPACE " \t\n\r\f" #ifndef MIN /* some macros */ # define MIN( i, j ) ( i > j ? j : i ) #endif #define REPLSTR(s,v) { \ if ((s) != NULL) { \ FREE(s); \ (s)=NULL; \ } \ (s) = STRDUP(v); \ if ((s) == NULL) { \ PILCallLog(PluginImports->log, \ PIL_CRIT, "out of memory"); \ } \ } #ifndef DEVICE #define DEVICE "Dummy" #endif #define PIL_PLUGINTYPE STONITH_TYPE #define PIL_PLUGINTYPE_S STONITH_TYPE_S #define ISCORRECTDEV(i) ((i)!= NULL \ && ((struct pluginDevice *)(i))->pluginid == pluginid) #define ERRIFWRONGDEV(s, retval) if (!ISCORRECTDEV(s)) { \ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \ return(retval); \ } #define VOIDERRIFWRONGDEV(s) if (!ISCORRECTDEV(s)) { \ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \ return; \ } #define ISCONFIGED(i) (i->isconfigured) #define ERRIFNOTCONFIGED(s,retval) ERRIFWRONGDEV(s,retval); \ if (!ISCONFIGED(s)) { \ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \ return(retval); \ } #define VOIDERRIFNOTCONFIGED(s) VOIDERRIFWRONGDEV(s); \ if (!ISCONFIGED(s)) { \ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \ return; \ } #endif Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/stonith_signal.h0000644000000000000000000000353312120057602030402 0ustar00usergroup00000000000000/* * stonith_signal.h: signal handling routines to be used by stonith * plugin libraries * * Copyright (C) 2002 Horms * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _STONITH_SIGNAL_H #define _STONITH_SIGNAL_H #include #include int stonith_signal_set_simple_handler(int sig, void (*handler)(int) , struct sigaction *oldact); int stonith_signal_set_simple_handler(int sig, void (*handler)(int) , struct sigaction *oldact) { struct sigaction sa; sigset_t mask; (void)stonith_signal_set_simple_handler; if(sigemptyset(&mask) < 0) { return(-1); } sa.sa_handler = handler; sa.sa_mask = mask; sa.sa_flags = 0; if(sigaction(sig, &sa, oldact) < 0) { return(-1); } return(0); } #define STONITH_SIGNAL(_sig, _handler) \ stonith_signal_set_simple_handler((_sig), (_handler), NULL) #ifdef HAVE_SIGIGNORE #define STONITH_IGNORE_SIG(_sig) \ sigignore((_sig)) #else #define STONITH_IGNORE_SIG(_sig) \ STONITH_SIGNAL((_sig), SIG_IGN) #endif #define STONITH_DEFAULT_SIG(_sig) STONITH_SIGNAL((_sig), SIG_DFL) #define STONITH_KILL(_pid, _sig) kill((_pid), (_sig)) #endif /* _STONITH_SIGNAL_H */ Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/suicide.c0000644000000000000000000001547012120057602027000 0ustar00usergroup00000000000000/* File: suicide.c * Description: Stonith module for suicide * * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #define DEVICE "Suicide STONITH device" #include "stonith_plugin_common.h" #define PIL_PLUGIN suicide #define PIL_PLUGIN_S "suicide" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * suicide_new(const char *); static void suicide_destroy(StonithPlugin *); static const char * const * suicide_get_confignames(StonithPlugin *); static int suicide_set_config(StonithPlugin *, StonithNVpair*); static const char * suicide_get_info(StonithPlugin * s, int InfoType); static int suicide_status(StonithPlugin * ); static int suicide_reset_req(StonithPlugin * s, int request , const char * host); static char ** suicide_hostlist(StonithPlugin *); static struct stonith_ops suicideOps ={ suicide_new, /* Create new STONITH object */ suicide_destroy, /* Destroy STONITH object */ suicide_get_info, /* Return STONITH info string */ suicide_get_confignames, /* Return configuration parameters */ suicide_set_config, /* Set configuration */ suicide_status, /* Return STONITH device status */ suicide_reset_req, /* Request a reset */ suicide_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &suicideOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } #define REBOOT_COMMAND "nohup sh -c 'sleep 2; " REBOOT " " REBOOT_OPTIONS " /dev/null 2>&1' &" #define POWEROFF_COMMAND "nohup sh -c 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS " /dev/null 2>&1' &" /* #define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1" #define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1" */ /* * Suicide STONITH device */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; }; static const char * pluginid = "SuicideDevice-Stonith"; static const char * NOTpluginid = "Suicide device has been destroyed"; #include "stonith_config_xml.h" static const char *suicideXML = XML_PARAMETERS_BEGIN XML_PARAMETERS_END; static int suicide_status(StonithPlugin *s) { ERRIFWRONGDEV(s, S_OOPS); return S_OK; } /* * Return the list of hosts configured for this Suicide device */ static char ** suicide_hostlist(StonithPlugin *s) { char** ret = NULL; struct utsname name; ERRIFWRONGDEV(s, NULL); if (uname(&name) == -1) { LOG(PIL_CRIT, "uname error %d", errno); return ret; } ret = OurImports->StringToHostList(name.nodename); if (ret == NULL) { LOG(PIL_CRIT, "out of memory"); return ret; } g_strdown(ret[0]); return ret; } /* * Suicide - reset or poweroff itself. */ static int suicide_reset_req(StonithPlugin * s, int request, const char * host) { int rc = -1; struct utsname name; ERRIFWRONGDEV(s, S_OOPS); if (request == ST_POWERON) { LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE); return S_INVAL; } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) { LOG(PIL_CRIT, "As for suicide virtual stonith device, " "reset request=%d is not supported", request); return S_INVAL; } if (uname(&name) == -1) { LOG(PIL_CRIT, "uname error %d", errno); return S_RESETFAIL ; } if (strcmp(name.nodename, host)) { LOG(PIL_CRIT, "%s doesn't control host [%s]" , name.nodename, host); return S_RESETFAIL ; } LOG(PIL_INFO, "Initiating suicide on host %s", host); rc = system( request == ST_GENERIC_RESET ? REBOOT_COMMAND : POWEROFF_COMMAND); if (rc == 0) { LOG(PIL_INFO, "Suicide stonith succeeded."); return S_OK; } else { LOG(PIL_CRIT, "Suicide stonith failed."); return S_RESETFAIL ; } } static const char * const * suicide_get_confignames(StonithPlugin* p) { /* Donnot need to initialize from external. */ static const char * SuicideParams[] = { NULL }; return SuicideParams; } /* * Parse the config information in the given string, and stash it away... */ static int suicide_set_config(StonithPlugin* s, StonithNVpair* list) { ERRIFWRONGDEV(s,S_OOPS); return S_OK; } static const char * suicide_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice* sd = (struct pluginDevice *)s; const char * ret; ERRIFWRONGDEV(s, NULL); sd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = sd->idinfo; break; case ST_DEVICENAME: ret = "suicide STONITH device"; break; case ST_DEVICEDESCR: /* Description of device type */ ret = "Virtual device to reboot/powerdown itself.\n"; break; case ST_CONF_XML: /* XML metadata */ ret = suicideXML; break; default: ret = NULL; break; } return ret; } /* * Suicide Stonith destructor... */ static void suicide_destroy(StonithPlugin *s) { struct pluginDevice* sd; VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice *)s; sd->pluginid = NOTpluginid; FREE(sd); } /* Create a new suicide Stonith device */ static StonithPlugin* suicide_new(const char * subplugin) { struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice); if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; sd->idinfo = DEVICE; sd->sp.s_ops = &suicideOps; return &(sd->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/vacm.c0000644000000000000000000003027112120057602026275 0ustar00usergroup00000000000000 /****************************************************************************** * * Copyright 2000 Sistina Software, Inc. * Tiny bits Copyright 2000 Alan Robertson * Tiny bits Copyright 2000 Zac Sprackett, VA Linux Systems * Tiny bits Copyright 2005 International Business Machines * Significantly Mangled by Sun Jiang Dong , IBM, 2005 * * This is free software released under the GNU General Public License. * There is no warranty for this software. See the file COPYING for * details. * * See the file CONTRIBUTORS for a list of contributors. * * This file is maintained by: * Michael C Tilstra * * Becasue I have no device to test, now I just make it pass the compiling * with vacm-2.0.5a. Please review before using. * Sun Jiang Dong , IBM, 2005 * * This module provides a driver for the VA Linux Cluster Manager. * For more information on VACM, see http://vacm.sourceforge.net/ * * This module is rather poorly commented. But if you've read the * VACM Manual, and looked at the code example they have, this * should make pretty clean sense. (You obiviously should have * looked at the other stonith source too) * */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define DEVICE "VA Linux Cluster Manager" #include "stonith_plugin_common.h" #include "vacmclient_api.h" #define PIL_PLUGIN vacm #define PIL_PLUGIN_S "vacm" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include static StonithPlugin * vacm_new(const char *); static void vacm_destroy(StonithPlugin *); static const char * const * vacm_get_confignames(StonithPlugin *); static int vacm_set_config(StonithPlugin *, StonithNVpair *); static const char * vacm_getinfo(StonithPlugin * s, int InfoType); static int vacm_status(StonithPlugin * ); static int vacm_reset_req(StonithPlugin * s, int request, const char * host); static char ** vacm_hostlist(StonithPlugin *); static struct stonith_ops vacmOps ={ vacm_new, /* Create new STONITH object */ vacm_destroy, /* Destroy STONITH object */ vacm_getinfo, /* Return STONITH info string */ vacm_get_confignames, /* Return configuration parameters */ vacm_set_config, /* Set configuration */ vacm_status, /* Return STONITH device status */ vacm_reset_req, /* Request a reset */ vacm_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug); static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &vacmOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /*structs*/ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; void *h; /* a handle to the nexxus. */ char * nexxus; char * user; char * passwd; }; #define ST_NEXXUS "nexxus" static const char * pluginid = "VACMDevice-Stonith"; static const char * NOTpluginid = "VACM device has been destroyed"; #include "stonith_config_xml.h" #define XML_NEXXUS_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_NEXXUS \ XML_PARM_SHORTDESC_END #define XML_NEXXUS_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The Nexxus component of the VA Cluster Manager" \ XML_PARM_LONGDESC_END #define XML_NEXXUS_PARM \ XML_PARAMETER_BEGIN(ST_NEXXUS, "string", "1") \ XML_NEXXUS_SHORTDESC \ XML_NEXXUS_LONGDESC \ XML_PARAMETER_END static const char *vacmXML = XML_PARAMETERS_BEGIN XML_NEXXUS_PARM XML_LOGIN_PARM XML_PASSWD_PARM XML_PARAMETERS_END; /*funcs*/ int vacm_status(StonithPlugin *s) { struct pluginDevice *sd; char snd[] = "NEXXUS:VERSION"; char *rcv, *tk; int rcvlen; ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*)s; /* If grabbing the nexxus version works, then the status must be ok. * right? */ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1); while(1) { if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) { break; } if (!(tk = strtok(rcv,":"))) { /*NEXXUS*/ break; }else if (!(tk=strtok(NULL,":"))) { /* Job ID */ break; }else if (!(tk=strtok(NULL,":"))) { /* one of the below */ break; } else if ( !strcmp(tk, "JOB_COMPLETED")) { free(rcv); return S_OK; /* YEAH!! */ }else if(!strcmp(tk, "JOB_STARTED")) { free(rcv); continue; }else if(!strcmp(tk, "JOB_ERROR")) { free(rcv); break; }else if(!strcmp(tk, "VERSION")) { free(rcv); continue; } else { LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n" , tk, rcv); break; } } return S_OOPS; } /* Better make sure the current group is correct. * Can't think of a good way to do this. */ char ** vacm_hostlist(StonithPlugin *s) { struct pluginDevice *sd; char snd[] = "NEXXUS:NODE_LIST"; char *rcv,*tk; int rcvlen; char ** hlst=NULL; int hacnt=0, hrcnt=0; #define MSTEP 20 ERRIFWRONGDEV(s, NULL); sd = (struct pluginDevice*)s; hlst = (char **)MALLOC(MSTEP * sizeof(char*)); if (hlst == NULL) { LOG(PIL_CRIT, "out of memory"); return NULL; } hacnt=MSTEP; api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1); while(1) { if(api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) { goto HL_cleanup; } if(!(tk=strtok(rcv, ":"))) { /* NEXXUS */ goto HL_cleanup; }else if(!(tk=strtok(NULL,":"))) { /* Job ID */ goto HL_cleanup; }else if(!(tk=strtok(NULL,":"))) { /* JOB_* or NODELIST */ goto HL_cleanup; }else if( !strcmp(tk, "JOB_STARTED")) { free(rcv); continue; }else if( !strcmp(tk, "JOB_COMPLETED")) { free(rcv); return hlst; }else if( !strcmp(tk, "JOB_ERROR")) { free(rcv); break; }else if( !strcmp(tk, "NODELIST")) { if(!(tk = strtok(NULL,":"))) { /* group */ goto HL_cleanup; }else if((tk = strtok(NULL," \t\n\r"))) { /*Finally, a machine name.*/ if( hrcnt >= (hacnt-1)) { /* grow array. */ char **oldhlst = hlst; hlst = (char **)REALLOC(hlst, (hacnt +MSTEP)*sizeof(char*)); if( !hlst ) { stonith_free_hostlist(oldhlst); return NULL; } hacnt += MSTEP; } hlst[hrcnt] = STRDUP(tk); /* stuff the name. */ hlst[hrcnt+1] = NULL; /* set next to NULL for looping */ if (hlst[hrcnt] == NULL) { stonith_free_hostlist(hlst); return NULL; } g_strdown(hlst[hrcnt]); hrcnt++; } }else { /* WTF?! */ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n",tk,rcv); break; } } HL_cleanup: stonith_free_hostlist(hlst); /* give the mem back */ return NULL; } #define SND_SIZE 256 int vacm_reset_req(StonithPlugin *s, int request, const char *host) { struct pluginDevice *sd; char snd[SND_SIZE]; /* god forbid its bigger than this */ char *rcv, *tk; int rcvlen; ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*)s; switch(request) { #ifdef ST_POWERON case ST_POWERON: snprintf(snd, SND_SIZE, "EMP:POWER_ON:%s", host); break; #endif /*ST_POWERON*/ #ifdef ST_POWEROFF case ST_POWEROFF: snprintf(snd, SND_SIZE, "EMP:POWER_OFF:%s", host); break; #endif /*ST_POWEROFF*/ case ST_GENERIC_RESET: snprintf(snd, SND_SIZE, "EMP:POWER_CYCLE:%s", host); break; default: return S_INVAL; } api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1); while(1) { if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) { return S_RESETFAIL; } if (!(tk = strtok(rcv,":"))) { /*EMP*/ break; }else if (!(tk=strtok(NULL,":"))) { /* Job ID */ break; }else if (!(tk=strtok(NULL,":"))) { /* one of teh below */ break; } else if ( !strcmp(tk, "JOB_COMPLETED")) { free(rcv); return S_OK; } else if(!strcmp(tk, "JOB_STARTED")) { free(rcv); continue; } else if(!strcmp(tk, "JOB_ERROR")) { free(rcv); return S_RESETFAIL; } else { /* WTF?! */ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n" , tk, rcv); break; } } return S_RESETFAIL; } /* list => "nexxus:username:password" */ static const char * const * vacm_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_NEXXUS, ST_LOGIN, ST_PASSWD, NULL}; return ret; } static int vacm_set_config(StonithPlugin *s, StonithNVpair * list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; StonithNamesToGet namestocopy [] = { {ST_NEXXUS, NULL} , {ST_LOGIN, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; char *rcv; int rcvlen; ERRIFWRONGDEV(s, S_OOPS); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->nexxus = namestocopy[0].s_value; sd->user = namestocopy[1].s_value; sd->passwd = namestocopy[2].s_value; /* When to initialize the sd->h */ if (api_nexxus_connect(sd->nexxus, sd->user, sd->passwd, &sd->h)<0){ return S_OOPS; } if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) { return S_OOPS; } if (strcmp(rcv, "NEXXUS_READY")) { rc = S_BADCONFIG; }else{ rc = S_OK; } free(rcv); return(rc); } /* * The "vacmconf:" is in the conffile so that one file could be used for * multiple device configs. This module will only look at the first line * that starts with this token. All other line are ignored. (and thus * could contain configs for other modules.) * * I don't think any other stonith modules do this currently. */ const char * vacm_getinfo(StonithPlugin *s, int reqtype) { struct pluginDevice* sd = (struct pluginDevice *)s; const char * ret; ERRIFWRONGDEV(s, NULL); switch (reqtype) { case ST_DEVICEID: /* What type of device? */ ret = sd->idinfo; break; case ST_DEVICENAME: /* Which particular device? */ ret = dgettext(ST_TEXTDOMAIN, "VACM"); break; case ST_DEVICEDESCR: /* Description of dev type */ ret = "A driver for the VA Linux Cluster Manager."; break; case ST_DEVICEURL: /* VACM's web site */ ret = "http://vacm.sourceforge.net/"; break; case ST_CONF_XML: /* XML metadata */ ret = vacmXML; break; default: ret = NULL; break; } return ret; } void vacm_destroy(StonithPlugin *s) { struct pluginDevice *sd; VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice*)s; if( sd->h ) { api_nexxus_disconnect(sd->h); } sd->pluginid = NOTpluginid; if (sd->nexxus != NULL) { FREE(sd->nexxus); sd->nexxus = NULL; } if (sd->user != NULL) { FREE(sd->user); sd->user = NULL; } if (sd->passwd != NULL) { FREE(sd->passwd); sd->passwd = NULL; } FREE(sd); } static StonithPlugin * vacm_new(const char *subplugin) { struct pluginDevice *sd; sd = MALLOC(sizeof(struct pluginDevice)); if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->h = NULL; sd->pluginid = pluginid; sd->nexxus = NULL; sd->user = NULL; sd->passwd = NULL; sd->idinfo = DEVICE; sd->sp.s_ops = &vacmOps; return &(sd->sp); /* same as "sd" */ } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/wti_mpc.c0000644000000000000000000005231612120057602027015 0ustar00usergroup00000000000000/* * Stonith module for WTI MPC (SNMP) * Copyright (c) 2001 Andreas Piesk * Mangled by Sun Jiang Dong , IBM, 2005 * * Modified for WTI MPC by Denis Chapligin , SatGate, 2009 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include /* device ID */ #define DEVICE "WTI MPC" #include "stonith_plugin_common.h" #undef FREE /* defined by snmp stuff */ #ifdef PACKAGE_BUGREPORT #undef PACKAGE_BUGREPORT #endif #ifdef PACKAGE_NAME #undef PACKAGE_NAME #endif #ifdef PACKAGE_STRING #undef PACKAGE_STRING #endif #ifdef PACKAGE_TARNAME #undef PACKAGE_TARNAME #endif #ifdef PACKAGE_VERSION #undef PACKAGE_VERSION #endif #ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H # include # include # include # define INIT_AGENT() init_master_agent() #else # include # include # include # ifndef NETSNMP_DS_APPLICATION_ID # define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID # endif # ifndef NETSNMP_DS_AGENT_ROLE # define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE # endif # define netsnmp_ds_set_boolean ds_set_boolean # define INIT_AGENT() init_master_agent(161, NULL, NULL) #endif #define PIL_PLUGIN wti_mpc #define PIL_PLUGIN_S "wti_mpc" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #define DEBUGCALL \ if (Debug) { \ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \ } static StonithPlugin * wti_mpc_new(const char *); static void wti_mpc_destroy(StonithPlugin *); static const char * const * wti_mpc_get_confignames(StonithPlugin *); static int wti_mpc_set_config(StonithPlugin *, StonithNVpair *); static const char * wti_mpc_getinfo(StonithPlugin * s, int InfoType); static int wti_mpc_status(StonithPlugin * ); static int wti_mpc_reset_req(StonithPlugin * s, int request, const char * host); static char ** wti_mpc_hostlist(StonithPlugin *); static struct stonith_ops wti_mpcOps ={ wti_mpc_new, /* Create new STONITH object */ wti_mpc_destroy, /* Destroy STONITH object */ wti_mpc_getinfo, /* Return STONITH info string */ wti_mpc_get_confignames, /* Get configuration parameters */ wti_mpc_set_config, /* Set configuration */ wti_mpc_status, /* Return STONITH device status */ wti_mpc_reset_req, /* Request a reset */ wti_mpc_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; DEBUGCALL; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &wti_mpcOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * APCMaster tested with APC Masterswitch 9212 */ /* outlet commands / status codes */ #define OUTLET_ON 5 #define OUTLET_OFF 6 #define OUTLET_REBOOT 7 /* oids */ #define OID_IDENT ".1.3.6.1.2.1.1.5.0" #define OID_GROUP_NAMES_V1 ".1.3.6.1.4.1.2634.3.1.3.1.2.%u" #define OID_GROUP_STATE_V1 ".1.3.6.1.4.1.2634.3.1.3.1.3.%i" #define OID_GROUP_NAMES_V3 ".1.3.6.1.4.1.2634.3.100.300.1.2.%u" #define OID_GROUP_STATE_V3 ".1.3.6.1.4.1.2634.3.100.300.1.3.%i" #define MAX_OUTLETS 128 /* snmpset -c private -v1 172.16.0.32:161 ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1 The last octet in the OID is the plug number. The value can be 1 thru 8 because there are 8 power plugs on this device. The integer that can be set is as follows: 1=on, 2=off, and 3=reset */ /* own defines */ #define MAX_STRING 128 #define ST_PORT "port" #define ST_MIBVERSION "mib-version" /* structur of stonith object */ struct pluginDevice { StonithPlugin sp; /* StonithPlugin object */ const char* pluginid; /* id of object */ const char* idinfo; /* type of device */ struct snmp_session* sptr; /* != NULL->session created */ char * hostname; /* masterswitch's hostname */ /* or ip addr */ int port; /* snmp port */ int mib_version; /* mib version to use */ char * community; /* snmp community (r/w) */ int num_outlets; /* number of outlets */ }; /* constant strings */ static const char *pluginid = "WTI-MPC-Stonith"; static const char *NOTpluginID = "WTI MPC device has been destroyed"; #include "stonith_config_xml.h" #define XML_PORT_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_PORT \ XML_PARM_SHORTDESC_END #define XML_PORT_LONGDESC \ XML_PARM_LONGDESC_BEGIN("en") \ "The port number on which the SNMP server is running on the STONITH device" \ XML_PARM_LONGDESC_END #define XML_PORT_PARM \ XML_PARAMETER_BEGIN(ST_PORT, "string", "1") \ XML_PORT_SHORTDESC \ XML_PORT_LONGDESC \ XML_PARAMETER_END #define XML_MIBVERSION_SHORTDESC \ XML_PARM_SHORTDESC_BEGIN("en") \ ST_MIBVERSION \ XML_PARM_SHORTDESC_END #define XML_MIBVERSION_LONGDESC \ XML_MIBVERSION_LONGDESC_BEGIN("en") \ "Version number of MPC MIB that we should use. Valid values are 1 (for 1.44 firmware) and 3 (for 1.62 firmware and later)" \ XML_PARM_LONGDESC_END #define XML_MIBVERSION_PARM \ XML_PARAMETER_BEGIN(ST_MIBVERSION, "string", "1") \ XML_PORT_SHORTDESC \ XML_PORT_LONGDESC \ XML_PARAMETER_END static const char *apcmastersnmpXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_PORT_PARM XML_COMMUNITY_PARM XML_MIBVERSION_PARM XML_PARAMETERS_END; /* * own prototypes */ static void MPC_error(struct snmp_session *sptr, const char *fn , const char *msg); static struct snmp_session *MPC_open(char *hostname, int port , char *community); static void *MPC_read(struct snmp_session *sptr, const char *objname , int type); static int MPC_write(struct snmp_session *sptr, const char *objname , char type, char *value); static void MPC_error(struct snmp_session *sptr, const char *fn, const char *msg) { int snmperr = 0; int cliberr = 0; char *errstr; snmp_error(sptr, &cliberr, &snmperr, &errstr); LOG(PIL_CRIT , "%s: %s (cliberr: %i / snmperr: %i / error: %s)." , fn, msg, cliberr, snmperr, errstr); free(errstr); } /* * creates a snmp session */ static struct snmp_session * MPC_open(char *hostname, int port, char *community) { static struct snmp_session session; struct snmp_session *sptr; DEBUGCALL; /* create session */ snmp_sess_init(&session); /* fill session */ session.peername = hostname; session.version = SNMP_VERSION_1; session.remote_port = port; session.community = (u_char *)community; session.community_len = strlen(community); session.retries = 5; session.timeout = 1000000; /* open session */ sptr = snmp_open(&session); if (sptr == NULL) { MPC_error(&session, __FUNCTION__, "cannot open snmp session"); } /* return pointer to opened session */ return (sptr); } /* * parse config */ /* * read value of given oid and return it as string */ static void * MPC_read(struct snmp_session *sptr, const char *objname, int type) { oid name[MAX_OID_LEN]; size_t namelen = MAX_OID_LEN; struct variable_list *vars; struct snmp_pdu *pdu; struct snmp_pdu *resp; static char response_str[MAX_STRING]; static int response_int; DEBUGCALL; /* convert objname into oid; return NULL if invalid */ if (!read_objid(objname, name, &namelen)) { LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname); return (NULL); } /* create pdu */ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) { /* get-request have no values */ snmp_add_null_var(pdu, name, namelen); /* send pdu and get response; return NULL if error */ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) { /* request succeed, got valid response ? */ if (resp->errstat == SNMP_ERR_NOERROR) { /* go through the returned vars */ for (vars = resp->variables; vars; vars = vars->next_variable) { /* return response as string */ if ((vars->type == type) && (type == ASN_OCTET_STR)) { memset(response_str, 0, MAX_STRING); strncpy(response_str, (char *)vars->val.string, MIN(vars->val_len, MAX_STRING)); snmp_free_pdu(resp); return ((void *) response_str); } /* return response as integer */ if ((vars->type == type) && (type == ASN_INTEGER)) { response_int = *vars->val.integer; snmp_free_pdu(resp); return ((void *) &response_int); } } }else{ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]." , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat)); } }else{ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu"); } /* free repsonse pdu (necessary?) */ snmp_free_pdu(resp); }else{ MPC_error(sptr, __FUNCTION__, "cannot create pdu"); } /* error: return nothing */ return (NULL); } /* * write value of given oid */ static int MPC_write(struct snmp_session *sptr, const char *objname, char type, char *value) { oid name[MAX_OID_LEN]; size_t namelen = MAX_OID_LEN; struct snmp_pdu *pdu; struct snmp_pdu *resp; DEBUGCALL; /* convert objname into oid; return FALSE if invalid */ if (!read_objid(objname, name, &namelen)) { LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname); return (FALSE); } /* create pdu */ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) { /* add to be written value to pdu */ snmp_add_var(pdu, name, namelen, type, value); /* send pdu and get response; return NULL if error */ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) { /* go through the returned vars */ if (resp->errstat == SNMP_ERR_NOERROR) { /* request successful done */ snmp_free_pdu(resp); return (TRUE); }else{ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]." , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat)); } }else{ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu"); } /* free pdu (again: necessary?) */ snmp_free_pdu(resp); }else{ MPC_error(sptr, __FUNCTION__, "cannot create pdu"); } /* error */ return (FALSE); } /* * return the status for this device */ static int wti_mpc_status(StonithPlugin * s) { struct pluginDevice *ad; char *ident; DEBUGCALL; ERRIFNOTCONFIGED(s, S_OOPS); ad = (struct pluginDevice *) s; if ((ident = MPC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__); return (S_ACCESS); } /* status ok */ return (S_OK); } /* * return the list of hosts configured for this device */ static char ** wti_mpc_hostlist(StonithPlugin * s) { char **hl; struct pluginDevice *ad; int j, h, num_outlets; char *outlet_name; char objname[MAX_STRING]; DEBUGCALL; ERRIFNOTCONFIGED(s, NULL); ad = (struct pluginDevice *) s; /* allocate memory for array of up to NUM_OUTLETS strings */ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); return (NULL); } /* clear hostlist array */ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *)); num_outlets = 0; /* read NUM_OUTLETS values and put them into hostlist array */ for (j = 0; j < ad->num_outlets; ++j) { /* prepare objname */ switch (ad->mib_version) { case 3: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,j+1); break; case 1: default: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,j+1); break; } if (Debug) { LOG(PIL_DEBUG, "%s: using %s as group names oid", __FUNCTION__, objname); } /* read outlet name */ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read name for outlet %d." , __FUNCTION__, j+1); stonith_free_hostlist(hl); hl = NULL; return (hl); } /* Check whether the host is already listed */ for (h = 0; h < num_outlets; ++h) { if (strcasecmp(hl[h],outlet_name) == 0) break; } if (h >= num_outlets) { /* put outletname in hostlist */ if (Debug) { LOG(PIL_DEBUG, "%s: added %s to hostlist." , __FUNCTION__, outlet_name); } if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); stonith_free_hostlist(hl); hl = NULL; return (hl); } g_strdown(hl[num_outlets]); num_outlets++; } } if (Debug) { LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets." , __FUNCTION__, num_outlets, j); } /* return list */ return (hl); } /* * reset the host */ static int wti_mpc_reset_req(StonithPlugin * s, int request, const char *host) { struct pluginDevice *ad; char objname[MAX_STRING]; char value[MAX_STRING]; char *outlet_name; int req_oid = OUTLET_REBOOT; int outlet; int found_outlet=-1; DEBUGCALL; ERRIFNOTCONFIGED(s, S_OOPS); ad = (struct pluginDevice *) s; /* read max. as->num_outlets values */ for (outlet = 1; outlet <= ad->num_outlets; outlet++) { /* prepare objname */ switch (ad->mib_version) { case 3: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,outlet); break; case 1: default: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,outlet); break; } /* read outlet name */ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT, "%s: cannot read name for outlet %d." , __FUNCTION__, outlet); return (S_ACCESS); } if (Debug) { LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name); } /* found one */ if (strcasecmp(outlet_name, host) == 0) { if (Debug) { LOG(PIL_DEBUG, "%s: found %s at outlet %d." , __FUNCTION__, host, outlet); } /* Ok, stop iterating over host list */ found_outlet=outlet; break; } } if (Debug) { LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet); } /* host not found in outlet names */ if (found_outlet == -1) { LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host); return (S_BADHOST); } /* choose the OID for the stonith request */ switch (request) { case ST_POWERON: req_oid = OUTLET_ON; break; case ST_POWEROFF: req_oid = OUTLET_OFF; break; case ST_GENERIC_RESET: req_oid = OUTLET_REBOOT; break; default: break; } /* Turn them all off */ /* prepare objnames */ switch (ad->mib_version) { case 3: snprintf(objname,MAX_STRING,OID_GROUP_STATE_V3,found_outlet); break; case 1: default: snprintf(objname,MAX_STRING,OID_GROUP_STATE_V1,found_outlet); break; } snprintf(value, MAX_STRING, "%i", req_oid); /* send reboot cmd */ if (!MPC_write(ad->sptr, objname, 'i', value)) { LOG(PIL_CRIT , "%s: cannot send reboot command for outlet %d." , __FUNCTION__, found_outlet); return (S_RESETFAIL); } return (S_OK); } /* * Get the configuration parameter names. */ static const char * const * wti_mpc_get_confignames(StonithPlugin * s) { static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, ST_MIBVERSION, NULL}; return ret; } /* * Set the configuration parameters. */ static int wti_mpc_set_config(StonithPlugin * s, StonithNVpair * list) { struct pluginDevice* sd = (struct pluginDevice *)s; int rc; char * i; int mo; char objname[MAX_STRING]; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {ST_PORT, NULL} , {ST_COMMUNITY, NULL} , {ST_MIBVERSION, NULL} , {NULL, NULL} }; DEBUGCALL; ERRIFWRONGDEV(s,S_INVAL); if (sd->sp.isconfigured) { return S_OOPS; } if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } sd->hostname = namestocopy[0].s_value; sd->port = atoi(namestocopy[1].s_value); PluginImports->mfree(namestocopy[1].s_value); sd->community = namestocopy[2].s_value; sd->mib_version = atoi(namestocopy[3].s_value); PluginImports->mfree(namestocopy[3].s_value); /* try to resolve the hostname/ip-address */ if (gethostbyname(sd->hostname) != NULL) { /* init snmp library */ init_snmp("wti_mpc"); /* now try to get a snmp session */ if ((sd->sptr = MPC_open(sd->hostname, sd->port, sd->community)) != NULL) { /* ok, get the number of groups from the mpc */ sd->num_outlets=0; /* We scan goup names table starting from 1 to MAX_OUTLETS */ /* and increase num_outlet counter on every group entry with name */ /* first entry without name is the mark of the end of the group table */ for (mo=1;momib_version) { case 3: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,mo); break; case 1: default: snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,mo); break; } if (Debug) { LOG(PIL_DEBUG, "%s: used for groupTable retrieval: %s." , __FUNCTION__, objname); } if ((i = MPC_read(sd->sptr, objname, ASN_OCTET_STR)) == NULL) { LOG(PIL_CRIT , "%s: cannot read number of outlets." , __FUNCTION__); return (S_ACCESS); } if (strlen(i)) { /* store the number of outlets */ sd->num_outlets++; } else { break; } } if (Debug) { LOG(PIL_DEBUG, "%s: number of outlets: %i." , __FUNCTION__, sd->num_outlets ); } /* Everything went well */ return (S_OK); }else{ LOG(PIL_CRIT, "%s: cannot create snmp session." , __FUNCTION__); } }else{ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d." , __FUNCTION__, sd->hostname, h_errno); } /* not a valid config */ return (S_BADCONFIG); } /* * get info about the stonith device */ static const char * wti_mpc_getinfo(StonithPlugin * s, int reqtype) { struct pluginDevice *ad; const char *ret = NULL; DEBUGCALL; ERRIFWRONGDEV(s, NULL); ad = (struct pluginDevice *) s; switch (reqtype) { case ST_DEVICEID: ret = ad->idinfo; break; case ST_DEVICENAME: ret = ad->hostname; break; case ST_DEVICEDESCR: ret = "WTI MPC (via SNMP)\n" "The WTI MPC can accept multiple simultaneous SNMP clients"; break; case ST_DEVICEURL: ret = "http://www.wti.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = apcmastersnmpXML; break; } return ret; } /* * APC StonithPlugin destructor... */ static void wti_mpc_destroy(StonithPlugin * s) { struct pluginDevice *ad; DEBUGCALL; VOIDERRIFWRONGDEV(s); ad = (struct pluginDevice *) s; ad->pluginid = NOTpluginID; /* release snmp session */ if (ad->sptr != NULL) { snmp_close(ad->sptr); ad->sptr = NULL; } /* reset defaults */ if (ad->hostname != NULL) { PluginImports->mfree(ad->hostname); ad->hostname = NULL; } if (ad->community != NULL) { PluginImports->mfree(ad->community); ad->community = NULL; } ad->num_outlets = 0; PluginImports->mfree(ad); } /* * Create a new APC StonithPlugin device. Too bad this function can't be * static */ static StonithPlugin * wti_mpc_new(const char *subplugin) { struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice); DEBUGCALL; /* no memory for stonith-object */ if (ad == NULL) { LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__); return (NULL); } /* clear stonith-object */ memset(ad, 0, sizeof(*ad)); /* set defaults */ ad->pluginid = pluginid; ad->sptr = NULL; ad->hostname = NULL; ad->community = NULL; ad->mib_version=1; ad->idinfo = DEVICE; ad->sp.s_ops = &wti_mpcOps; /* return the object */ return (&(ad->sp)); } Reusable-Cluster-Components-glue--3cff550e1084/lib/plugins/stonith/wti_nps.c0000644000000000000000000004476312120057602027045 0ustar00usergroup00000000000000/* * * Copyright 2001 Mission Critical Linux, Inc. * * All Rights Reserved. */ /* * Stonith module for WTI Network Power Switch Devices (NPS-xxx) * Also supports the WTI Telnet Power Switch Devices (TPS-xxx) * * Copyright 2001 Mission Critical Linux, Inc. * author: mike ledoux * author: Todd Wheeling * Mangled by Zhaokai , IBM, 2005 * Further hurt by Lon , Red Hat, 2005 * * Supported WTI devices: * NPS-115 * NPS-230 * IPS-15 * IPS-800 * IPS-800-CE * NBB-1600 * NBB-1600-CE * TPS-2 * * Based strongly on original code from baytech.c by Alan Robertson. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Observations/Notes * * 1. The WTI Network Power Switch, unlike the BayTech network power switch, * accpets only one (telnet) connection/session at a time. When one * session is active, any subsequent attempt to connect to the NPS will * result in a connection refused/closed failure. In a cluster environment * or other environment utilizing polling/monitoring of the NPS * (from multiple nodes), this can clearly cause problems. Obviously the * more nodes and the shorter the polling interval, the more frequently such * errors/collisions may occur. * * 2. We observed that on busy networks where there may be high occurances * of broadcasts, the NPS became unresponsive. In some * configurations this necessitated placing the power switch onto a * private subnet. */ #include #define DEVICE "WTI Network Power Switch" #define DOESNT_USE_STONITHKILLCOMM 1 #include "stonith_plugin_common.h" #define PIL_PLUGIN wti_nps #define PIL_PLUGIN_S "wti_nps" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #define MAX_WTIPLUGINID 256 #include #include "stonith_signal.h" static StonithPlugin * wti_nps_new(const char *); static void wti_nps_destroy(StonithPlugin *); static const char * const * wti_nps_get_confignames(StonithPlugin *); static int wti_nps_set_config(StonithPlugin * , StonithNVpair * ); static const char * wti_nps_get_info(StonithPlugin * s, int InfoType); static int wti_nps_status(StonithPlugin * ); static int wti_nps_reset_req(StonithPlugin * s, int request, const char * host); static char ** wti_nps_hostlist(StonithPlugin *); static struct stonith_ops wti_npsOps ={ wti_nps_new, /* Create new STONITH object */ wti_nps_destroy, /* Destroy STONITH object */ wti_nps_get_info, /* Return STONITH info string */ wti_nps_get_confignames,/* Return configration parameters */ wti_nps_set_config, /* set configration */ wti_nps_status, /* Return STONITH device status */ wti_nps_reset_req, /* Request a reset */ wti_nps_hostlist, /* Return list of supported hosts */ }; PIL_PLUGIN_BOILERPLATE2("1.0", Debug) static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; #include "stonith_expect_helpers.h" PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &wti_npsOps , NULL /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); } /* * I have a NPS-110. This code has been tested with this switch. * (Tested with NPS-230 and TPS-2 by lmb) */ struct pluginDevice { StonithPlugin sp; const char * pluginid; const char * idinfo; pid_t pid; int rdfd; int wrfd; char * device; char * passwd; }; static const char * pluginid = "WTINPS-Stonith"; static const char * NOTnpsid = "WTINPS device has been destroyed"; #include "stonith_config_xml.h" static const char *wti_npsXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_PASSWD_PARM XML_PARAMETERS_END; /* * Different expect strings that we get from the WTI * Network Power Switch */ #define WTINPSSTR " Power Switch" #define WTINBBSTR "Boot Bar" static struct Etoken password[] = { {"Password:", 0, 0}, {NULL,0,0}}; static struct Etoken Prompt[] = { {"PS>", 0, 0} , {"IPS>", 0, 0} , {"BB>", 0, 0} , {NULL,0,0}}; static struct Etoken LoginOK[] = { {WTINPSSTR, 0, 0} , {WTINBBSTR, 0, 0} , {"Invalid password", 1, 0} , {NULL,0,0} }; static struct Etoken Separator[] = { {"-----+", 0, 0} ,{NULL,0,0}}; /* We may get a notice about rebooting, or a request for confirmation */ static struct Etoken Processing[] = { {"rocessing - please wait", 0, 0} , {"(Y/N):", 1, 0} , {NULL,0,0}}; static int NPS_connect_device(struct pluginDevice * nps); static int NPSLogin(struct pluginDevice * nps); static int NPSNametoOutlet(struct pluginDevice*, const char * name, char **outlets); static int NPSReset(struct pluginDevice*, char * outlets, const char * rebootid); static int NPSLogout(struct pluginDevice * nps); #if defined(ST_POWERON) && defined(ST_POWEROFF) static int NPS_onoff(struct pluginDevice*, const char * outlets, const char * unitid , int request); #endif /* Attempt to login up to 20 times... */ static int NPSRobustLogin(struct pluginDevice * nps) { int rc = S_OOPS; int j = 0; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } for ( ; ; ) { if (NPS_connect_device(nps) == S_OK) { rc = NPSLogin(nps); if (rc == S_OK) { break; } } if ((++j) == 20) { break; } else { sleep(1); } } return rc; } /* Login to the WTI Network Power Switch (NPS) */ static int NPSLogin(struct pluginDevice * nps) { char IDinfo[128]; char * idptr = IDinfo; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* Look for the unit type info */ if (EXPECT_TOK(nps->rdfd, password, 2, IDinfo , sizeof(IDinfo), Debug) < 0) { LOG(PIL_CRIT, "No initial response from %s.", nps->idinfo); return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } idptr += strspn(idptr, WHITESPACE); /* * We should be looking at something like this: * Enter Password: */ SEND(nps->wrfd, nps->passwd); SEND(nps->wrfd, "\r"); /* Expect "Network Power Switch vX.YY" */ switch (StonithLookFor(nps->rdfd, LoginOK, 5)) { case 0: /* Good! */ LOG(PIL_INFO, "Successful login to %s.", nps->idinfo); break; case 1: /* Uh-oh - bad password */ LOG(PIL_CRIT, "Invalid password for %s.", nps->idinfo); return(S_ACCESS); default: return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } return(S_OK); } /* Log out of the WTI NPS */ static int NPSLogout(struct pluginDevice* nps) { int rc; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* Send "/h" help command and expect back prompt */ /* SEND(nps->wrfd, "/h\r"); */ /* Expect "PS>" */ rc = StonithLookFor(nps->rdfd, Prompt, 5); /* "/x" is Logout, "/x,y" auto-confirms */ SEND(nps->wrfd, "/x,y\r"); close(nps->wrfd); close(nps->rdfd); nps->wrfd = nps->rdfd = -1; return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS)); } /* Reset (power-cycle) the given outlets */ static int NPSReset(struct pluginDevice* nps, char * outlets, const char * rebootid) { char unum[32]; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* Send "/h" help command and expect back prompt */ SEND(nps->wrfd, "/h\r"); /* Expect "PS>" */ EXPECT(nps->rdfd, Prompt, 5); /* Send REBOOT command for given outlets */ snprintf(unum, sizeof(unum), "/BOOT %s,y\r", outlets); SEND(nps->wrfd, unum); /* Expect "Processing "... or "(Y/N)" (if confirmation turned on) */ retry: switch (StonithLookFor(nps->rdfd, Processing, 5)) { case 0: /* Got "Processing" Do nothing */ break; case 1: /* Got that annoying command confirmation :-( */ SEND(nps->wrfd, "Y\r"); goto retry; default: return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } LOG(PIL_INFO, "Host is being rebooted: %s", rebootid); /* Expect "PS>" */ if (StonithLookFor(nps->rdfd, Prompt, 60) < 0) { return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } /* All Right! Power is back on. Life is Good! */ LOG(PIL_INFO, "Power restored to host: %s", rebootid); SEND(nps->wrfd, "/h\r"); return(S_OK); } #if defined(ST_POWERON) && defined(ST_POWEROFF) static int NPS_onoff(struct pluginDevice* nps, const char * outlets, const char * unitid, int req) { char unum[32]; const char * onoff = (req == ST_POWERON ? "/On" : "/Off"); if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } /* Send "/h" help command and expect prompt back */ SEND(nps->wrfd, "/h\r"); /* Expect "PS>" */ EXPECT(nps->rdfd, Prompt, 5); /* Send ON/OFF command for given outlet */ snprintf(unum, sizeof(unum), "%s %s,y\r", onoff, outlets); SEND(nps->wrfd, unum); /* Expect "Processing"... or "(Y/N)" (if confirmation turned on) */ if (StonithLookFor(nps->rdfd, Processing, 5) == 1) { /* They've turned on that annoying command confirmation :-( */ SEND(nps->wrfd, "Y\r"); } EXPECT(nps->rdfd, Prompt, 60); /* All Right! Command done. Life is Good! */ LOG(PIL_INFO, "Power to NPS outlet(s) %s turned %s", outlets, onoff); SEND(nps->wrfd, "/h\r"); return(S_OK); } #endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */ /* * Map the given host name into an (AC) Outlet number on the power strip */ static int NPSNametoOutlet(struct pluginDevice* nps, const char * name, char **outlets) { char NameMapping[128]; int sockno; char sockname[32]; char buf[32]; int left = 17; int ret = -1; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } if ((*outlets = (char *)MALLOC(left*sizeof(char))) == NULL) { LOG(PIL_CRIT, "out of memory"); return(-1); } strncpy(*outlets, "", left); left = left - 1; /* ensure terminating '\0' */ /* Expect "PS>" */ EXPECT(nps->rdfd, Prompt, 5); /* The status command output contains mapping of hosts to outlets */ SEND(nps->wrfd, "/s\r"); /* Expect: "-----+" so we can skip over it... */ EXPECT(nps->rdfd, Separator, 5); do { NameMapping[0] = EOS; SNARF(nps->rdfd, NameMapping, 5); if (sscanf(NameMapping , "%d | %16c",&sockno, sockname) == 2) { char * last = sockname+16; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (strncasecmp(name, sockname, 16) == 0) { ret = sockno; snprintf(buf, sizeof(buf), "%d ", sockno); strncat(*outlets, buf, left); left = left - strlen(buf); } } } while (strlen(NameMapping) > 2 && left > 0); return(ret); } static int wti_nps_status(StonithPlugin *s) { struct pluginDevice* nps; int rc; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); nps = (struct pluginDevice*) s; if ((rc = NPSRobustLogin(nps) != S_OK)) { LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo); return(rc); } /* Send "/h" help command and expect back prompt */ SEND(nps->wrfd, "/h\r"); /* Expect "PS>" */ EXPECT(nps->rdfd, Prompt, 5); return(NPSLogout(nps)); } /* * Return the list of hosts (outlet names) for the devices on this NPS unit */ static char ** wti_nps_hostlist(StonithPlugin *s) { char NameMapping[128]; char* NameList[64]; unsigned int numnames = 0; char ** ret = NULL; struct pluginDevice* nps; unsigned int i; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,NULL); nps = (struct pluginDevice*) s; if (NPSRobustLogin(nps) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo); return(NULL); } /* Expect "PS>" */ NULLEXPECT(nps->rdfd, Prompt, 5); /* The status command output contains mapping of hosts to outlets */ SEND(nps->wrfd, "/s\r"); /* Expect: "-----" so we can skip over it... */ NULLEXPECT(nps->rdfd, Separator, 5); NULLEXPECT(nps->rdfd, CRNL, 5); /* Looks Good! Parse the status output */ do { int sockno; char sockname[64]; NameMapping[0] = EOS; NULLSNARF(nps->rdfd, NameMapping, 5); if (sscanf(NameMapping , "%d | %16c",&sockno, sockname) == 2) { char * last = sockname+16; char * nm; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (numnames >= DIMOF(NameList)-1) { break; } if (!strcmp(sockname,"(undefined)") || !strcmp(sockname,"---")) { /* lhh - skip undefined */ continue; } if ((nm = STRDUP(sockname)) == NULL) { goto out_of_memory; } g_strdown(nm); NameList[numnames] = nm; ++numnames; NameList[numnames] = NULL; } } while (strlen(NameMapping) > 2); if (numnames >= 1) { ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { goto out_of_memory; }else{ memset(ret, 0, (numnames+1)*sizeof(char*)); memcpy(ret, NameList, (numnames+1)*sizeof(char*)); } } (void)NPSLogout(nps); return(ret); out_of_memory: LOG(PIL_CRIT, "out of memory"); for (i=0; iOpenStreamSocket(nps->device , TELNET_PORT, TELNET_SERVICE); if (fd < 0) { return(S_OOPS); } nps->rdfd = nps->wrfd = fd; return(S_OK); } /* * Reset the given host on this Stonith device. */ static int wti_nps_reset_req(StonithPlugin * s, int request, const char * host) { int rc = 0; int lorc = 0; struct pluginDevice* nps; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFNOTCONFIGED(s,S_OOPS); nps = (struct pluginDevice*) s; if ((rc = NPSRobustLogin(nps)) != S_OK) { LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo); }else{ char *outlets; int noutlet; outlets = NULL; noutlet = NPSNametoOutlet(nps, host, &outlets); if (noutlet < 1) { LOG(PIL_WARN, "%s doesn't control host [%s]" , nps->device, host); if (outlets != NULL) { FREE(outlets); outlets = NULL; } return(S_BADHOST); } switch(request) { #if defined(ST_POWERON) && defined(ST_POWEROFF) case ST_POWERON: case ST_POWEROFF: rc = NPS_onoff(nps, outlets, host, request); if (outlets != NULL) { FREE(outlets); outlets = NULL; } break; #endif case ST_GENERIC_RESET: rc = NPSReset(nps, outlets, host); if (outlets != NULL) { FREE(outlets); outlets = NULL; } break; default: rc = S_INVAL; if (outlets != NULL) { FREE(outlets); outlets = NULL; } break; } } lorc = NPSLogout(nps); return(rc != S_OK ? rc : lorc); } /* * Parse the information in the given string, * and stash it away... */ static int wti_nps_set_config(StonithPlugin * s, StonithNVpair *list) { struct pluginDevice* nps; StonithNamesToGet namestocopy [] = { {ST_IPADDR, NULL} , {ST_PASSWD, NULL} , {NULL, NULL} }; int rc; if (Debug) { LOG(PIL_DEBUG, "%s: called.\n", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); nps = (struct pluginDevice*) s; if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { return rc; } nps->device = namestocopy[0].s_value; nps->passwd = namestocopy[1].s_value; return S_OK; } /* * Return the Stonith plugin configuration parameter * */ static const char * const * wti_nps_get_confignames(StonithPlugin * p) { static const char * names[] = { ST_IPADDR , ST_PASSWD , NULL}; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } return names; } /* * Get info about the stonith device * */ static const char * wti_nps_get_info(StonithPlugin * s, int reqtype) { struct pluginDevice* nps; const char * ret; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } ERRIFWRONGDEV(s,NULL); /* * We look in the ST_TEXTDOMAIN catalog for our messages */ nps = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = nps->idinfo; break; case ST_DEVICENAME: ret = nps->device; break; case ST_DEVICEDESCR: ret = "Western Telematic (WTI) Network Power Switch Devices (NPS-xxx)\n" "Also supports the WTI Telnet Power Switch Devices (TPS-xxx)\n" "NOTE: The WTI Network Power Switch, accepts only " "one (telnet) connection/session at a time."; break; case ST_DEVICEURL: ret = "http://www.wti.com/"; break; case ST_CONF_XML: /* XML metadata */ ret = wti_npsXML; break; default: ret = NULL; break; } return ret; } /* * WTI NPS Stonith destructor... */ static void wti_nps_destroy(StonithPlugin *s) { struct pluginDevice* nps; if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } VOIDERRIFWRONGDEV(s); nps = (struct pluginDevice *)s; nps->pluginid = NOTnpsid; if (nps->rdfd >= 0) { close(nps->rdfd); nps->rdfd = -1; } if (nps->wrfd >= 0) { close(nps->wrfd); nps->wrfd = -1; } if (nps->device != NULL) { FREE(nps->device); nps->device = NULL; } if (nps->passwd != NULL) { FREE(nps->passwd); nps->passwd = NULL; } FREE(nps); } /* Create a new BayTech Stonith device. */ static StonithPlugin * wti_nps_new(const char *subplugin) { struct pluginDevice* nps = ST_MALLOCT(struct pluginDevice); if (Debug) { LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); } if (nps == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(nps, 0, sizeof(*nps)); nps->pluginid = pluginid; nps->pid = -1; nps->rdfd = -1; nps->wrfd = -1; nps->device = NULL; nps->passwd = NULL; nps->idinfo = DEVICE; nps->sp.s_ops = &wti_npsOps; return &(nps->sp); } Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/Makefile.am0000644000000000000000000000344412120057602025560 0ustar00usergroup00000000000000# # Stonith: Shoot The Node In The Head # # Copyright (C) 2001 Alan Robertson # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl ## include files ## binaries sbin_PROGRAMS = stonith meatclient stonith_SOURCES = main.c stonith_LDADD = libstonith.la $(top_builddir)/lib/pils/libpils.la $(GLIBLIB) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/clplumbing/libplumbgpl.la stonith_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ @LIBADD_INTL@ meatclient_SOURCES = meatclient.c meatclient_LDADD = $(GLIBLIB) ## libraries lib_LTLIBRARIES = libstonith.la libstonith_la_SOURCES = expect.c stonith.c st_ttylock.c libstonith_la_LDFLAGS = -version-info 1:0:0 libstonith_la_LIBADD = $(top_builddir)/lib/pils/libpils.la \ $(top_builddir)/replace/libreplace.la \ $(GLIBLIB) helperdir = $(datadir)/$(PACKAGE_NAME) helper_SCRIPTS = ha_log.sh EXTRA_DIST = $(helper_SCRIPTS) Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/README0000644000000000000000000000236512120057602024405 0ustar00usergroup00000000000000The STONITH module (a.k.a. STOMITH) provides an extensible interface for remotely powering down a node in the cluster. The idea is quite simple: When the software running on one machine wants to make sure another machine in the cluster is not using a resource, pull the plug on the other machine. It's simple and reliable, albiet admittedly brutal. Here's an example command line invocation used to power off a machine named 'nodeb'. The parameters are dependent on the type of device you are using for this capability. stonith -t rps10 -p "/dev/ttyS5 nodeb 0 " nodeb Currently supported devices: apcsmart: APCSmart (tested with 2 old 900XLI) baytech: Baytech RPC5 meatware: Alerts an operator to manually turn off a device. nw_rpc100s: Micro Energetics Night/Ware RPC100S rps10: Western Telematics RPS10 vacm_stonith: VA Linux Cluster Manager (see README.vacm) To see the parameter syntax for a module, run the 'stonith' command and omit the -p parameter. For example: $ /usr/sbin/stonith -t rps10 test stonith: Invalid config file for rps10 device. stonith: Config file syntax: [ [...] ] All tokens are white-space delimited. Blank lines and lines beginning with # are ignored Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/expect.c0000644000000000000000000002725212120057602025163 0ustar00usergroup00000000000000/* * Simple expect module for the STONITH library * * Copyright (c) 2000 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #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 #define ENABLE_PIL_DEFS_PRIVATE #include #ifdef _POSIX_PRIORITY_SCHEDULING # include #endif #include #include extern PILPluginUniv* StonithPIsys; #define LOG(args...) PILCallLog(StonithPIsys->imports->log, args) #define DEBUG(args...) LOG(PIL_DEBUG, args) #undef DEBUG #define DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args) #define MALLOC StonithPIsys->imports->alloc #define REALLOC StonithPIsys->imports->mrealloc #define STRDUP StonithPIsys->imports->mstrdup #define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;} #ifdef TIMES_ALLOWS_NULL_PARAM # define TIMES_PARAM NULL #else static struct tms dummy_longclock_tms_struct; # define TIMES_PARAM &dummy_longclock_tms_struct #endif static unsigned long our_times(void) /* Make times(2) behave rationally on Linux */ { clock_t ret; #ifndef DISABLE_TIMES_KLUDGE int save_errno = errno; /* * This code copied from clplumbing/longclock.c to avoid * making STONITH depend on clplumbing. See it for an explanation */ errno = 0; #endif /* DISABLE_TIMES_KLUDGE */ ret = times(TIMES_PARAM); #ifndef DISABLE_TIMES_KLUDGE if (errno != 0) { ret = (clock_t) (-errno); } errno = save_errno; #endif /* DISABLE_TIMES_KLUDGE */ return (unsigned long)ret; } /* * Look for ('expect') any of a series of tokens in the input * Return the token type for the given token or -1 on error. */ static int ExpectToken(int fd, struct Etoken * toklist, int to_secs, char * savebuf , int maxline, int Debug) { unsigned long starttime; unsigned long endtime; int wraparound=0; unsigned Hertz = sysconf(_SC_CLK_TCK); int tickstousec = (1000000/Hertz); unsigned long now; unsigned long ticks; int nchars = 1; /* reserve space for an EOS */ struct timeval tv; char * buf = savebuf; struct Etoken * this; /* Figure out when to give up. Handle lbolt wraparound */ starttime = our_times(); ticks = (to_secs*Hertz); endtime = starttime + ticks; if (endtime < starttime) { wraparound = 1; } if (buf) { *buf = EOS; } for (this=toklist; this->string; ++this) { this->matchto = 0; } while (now = our_times(), (wraparound && (now > starttime || now <= endtime)) || (!wraparound && now <= endtime)) { fd_set infds; char ch; unsigned long timeleft; int retval; timeleft = endtime - now; tv.tv_sec = timeleft / Hertz; tv.tv_usec = (timeleft % Hertz) * tickstousec; if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) { /* Give 'em a little chance */ tv.tv_usec = tickstousec; } /* Watch our FD to see when it has input. */ FD_ZERO(&infds); FD_SET(fd, &infds); retval = select(fd+1, &infds, NULL, NULL, &tv); if (retval <= 0) { errno = ETIMEDOUT; return(-1); } /* Whew! All that work just to read one character! */ if (read(fd, &ch, sizeof(ch)) <= 0) { return(-1); } /* Save the text, if we can */ if (buf && nchars < maxline-1) { *buf = ch; ++buf; *buf = EOS; ++nchars; } if (Debug > 1) { DEBUG("Got '%c'", ch); } /* See how this character matches our expect strings */ for (this=toklist; this->string; ++this) { if (ch == this->string[this->matchto]) { /* It matches the current token */ ++this->matchto; if (this->string[this->matchto] == EOS){ /* Hallelujah! We matched */ if (Debug) { DEBUG("Matched [%s] [%d]" , this->string , this->toktype); if (savebuf) { DEBUG("Saved [%s]" , savebuf); } } return(this->toktype); } }else{ /* It doesn't appear to match this token */ int curlen; int nomatch=1; /* * If we already had a match (matchto is * greater than zero), we look for a match * of the tail of the pattern matched so far * (with the current character) against the * head of the pattern. */ /* * This is to make the string "aab" match * the pattern "ab" correctly * Painful, but nice to do it right. */ for (curlen = (this->matchto) ; nomatch && curlen >= 0 ; --curlen) { const char * tail; tail=(this->string) + this->matchto - curlen; if (strncmp(this->string, tail , curlen) == 0 && this->string[curlen] == ch) { if (this->string[curlen+1]==EOS){ /* We matched! */ /* (can't happen?) */ return(this->toktype); } this->matchto = curlen+1; nomatch=0; } } if (nomatch) { this->matchto = 0; } } } } errno = ETIMEDOUT; return(-1); } /* * Start a process with its stdin and stdout redirected to pipes * so the parent process can talk to it. */ static int StartProcess(const char * cmd, int * readfd, int * writefd) { pid_t pid; int wrpipe[2]; /* The pipe the parent process writes to */ /* (which the child process reads from) */ int rdpipe[2]; /* The pipe the parent process reads from */ /* (which the child process writes to) */ if (pipe(wrpipe) < 0) { perror("cannot create pipe\n"); return(-1); } if (pipe(rdpipe) < 0) { perror("cannot create pipe\n"); close(wrpipe[0]); close(wrpipe[1]); return(-1); } switch(pid=fork()) { case -1: perror("cannot StartProcess cmd"); close(rdpipe[0]); close(wrpipe[1]); close(wrpipe[0]); close(rdpipe[1]); return(-1); case 0: /* We are the child */ /* Redirect stdin */ close(0); dup2(wrpipe[0], 0); close(wrpipe[0]); close(wrpipe[1]); /* Redirect stdout */ close(1); dup2(rdpipe[1], 1); close(rdpipe[0]); close(rdpipe[1]); #if defined(SCHED_OTHER) && !defined(ON_DARWIN) { /* * Try and (re)set our scheduling to "normal" * Sometimes our callers run in soft * real-time mode. The program we exec might * not be very well behaved - this is bad for * operation in high-priority (soft real-time) * mode. In particular, telnet is prone to * going into infinite loops when killed. */ struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &sp); } #endif execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL); perror("cannot exec shell!"); exit(1); default: /* We are the parent */ *readfd = rdpipe[0]; close(rdpipe[1]); *writefd = wrpipe[1]; close(wrpipe[0]); return(pid); } /*NOTREACHED*/ return(-1); } static char ** stonith_copy_hostlist(const char * const * hostlist) { int hlleng = 1; const char * const * here = hostlist; char ** hret; char ** ret; for (here = hostlist; *here; ++here) { ++hlleng; } ret = (char**)MALLOC(hlleng * sizeof(char *)); if (ret == NULL) { return ret; } hret = ret; for (here = hostlist; *here; ++here,++hret) { *hret = STRDUP(*here); if (*hret == NULL) { stonith_free_hostlist(ret); return NULL; } } *hret = NULL; return ret; } static char ** StringToHostList(const char * s) { const char * here; int hlleng = 0; char ** ret; char ** hret; const char * delims = " \t\n\f\r,"; /* Count the number of strings (words) in the result */ here = s; while (*here != EOS) { /* skip delimiters */ here += strspn(here, delims); if (*here == EOS) { break; } /* skip over substring proper... */ here += strcspn(here, delims); ++hlleng; } /* Malloc space for the result string pointers */ ret = (char**)MALLOC((hlleng+1) * sizeof(char *)); if (ret == NULL) { return NULL; } hret = ret; here = s; /* Copy each substring into a separate string */ while (*here != EOS) { int slen; /* substring length */ /* skip delimiters */ here += strspn(here, delims); if (*here == EOS) { break; } /* Compute substring length */ slen = strcspn(here, delims); *hret = MALLOC((slen+1) * sizeof(char)); if (*hret == NULL) { stonith_free_hostlist(hret); return NULL; } /* Copy string (w/o EOS) */ memcpy(*hret, here, slen); /* Add EOS to result string */ (*hret)[slen] = EOS; g_strdown(*hret); here += slen; ++hret; } *hret = NULL; return ret; } static const char * GetValue(StonithNVpair* parameters, const char * name) { while (parameters->s_name) { if (strcmp(name, parameters->s_name) == 0) { return parameters->s_value; } ++parameters; } return NULL; } static int CopyAllValues(StonithNamesToGet* output, StonithNVpair * input) { int j; int rc; for (j=0; output[j].s_name; ++j) { const char * value = GetValue(input, output[j].s_name); if (value == NULL) { rc = S_INVAL; output[j].s_value = NULL; goto fail; } if ((output[j].s_value = STRDUP(value)) == NULL) { rc = S_OOPS; goto fail; } } return S_OK; fail: for (j=0; output[j].s_value; ++j) { FREE(output[j].s_value); } return rc; } static int OpenStreamSocket(const char * host, int port, const char * service) { union s_un { struct sockaddr_in si4; struct sockaddr_in6 si6; }sockun; int sock; int addrlen = -1; memset(&sockun, 0, sizeof(sockun)); if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) { sockun.si4.sin_family = AF_INET; }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){ sockun.si6.sin6_family = AF_INET6; }else{ struct hostent* hostp = gethostbyname(host); if (hostp == NULL) { errno = EINVAL; return -1; } sockun.si4.sin_family = hostp->h_addrtype; memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length); } if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) { return -1; } if (service != NULL) { struct servent* se = getservbyname(service, "tcp"); if (se != NULL) { /* We convert it back later... */ port = ntohs(se->s_port); } } if (port <= 0) { errno = EINVAL; return -1; } port = htons(port); if (sockun.si6.sin6_family == AF_INET6) { sockun.si6.sin6_port = port; addrlen = sizeof(sockun.si6); }else if (sockun.si4.sin_family == AF_INET) { sockun.si4.sin_port = port; addrlen = sizeof(sockun.si4); }else{ errno = EINVAL; return -1; } if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){ int save = errno; perror("connect() failed"); close(sock); errno = save; return -1; } return sock; } StonithImports stonithimports = { ExpectToken, StartProcess, OpenStreamSocket, GetValue, CopyAllValues, StringToHostList, stonith_copy_hostlist, stonith_free_hostlist, st_ttylock, st_ttyunlock }; Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/ha_log.sh0000755000000000000000000000474012120057602025314 0ustar00usergroup00000000000000#!/bin/sh # # # ha_log.sh for stonith external plugins # (equivalent to ocf_log in ocf-shellfuncs in resource-agents) # # Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée # All Rights Reserved. # # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Build version: @GLUE_BUILD_VERSION@ PROG=`basename $0` : ${HA_DATEFMT=+"%b %d %T"} : ${HA_LOGD=yes} : ${HA_LOGTAG=""} : ${HA_LOGFACILITY=daemon} : ${HA_LOGFILE=""} : ${HA_DEBUGLOG=""} : ${HA_debug="0"} hadate() { date "+$HA_DATEFMT" } level_pres() { case "$1" in crit) echo "CRIT";; err|error) echo "ERROR";; warn|warning) echo "WARN";; notice) echo "notice";; info) echo "info";; debug) echo "debug";; *) ha_log err "$PROG: unrecognized loglevel: $1" exit 1 ;; esac } set_logtag() { # add parent pid to the logtag if [ "$HA_LOGTAG" ]; then if [ -n "$CRM_meta_st_device_id" ]; then HA_LOGTAG="$HA_LOGTAG($CRM_meta_st_device_id)[$PPID]" else HA_LOGTAG="$HA_LOGTAG[$PPID]" fi fi } ha_log() { loglevel=$1 shift prn_level=`level_pres $loglevel` msg="$prn_level: $@" if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then return 0 fi set_logtag # if we're connected to a tty, then output to stderr if tty >/dev/null; then if [ "$HA_LOGTAG" ]; then echo "$HA_LOGTAG: $msg" else echo "$msg" fi >&2 return 0 fi [ "x$HA_LOGD" = "xyes" ] && cat<> $dest fi } if [ $# -lt 2 ]; then ha_log err "$PROG: not enough arguments [$#]" exit 1 fi loglevel="$1" shift 1 msg="$*" ha_log "$loglevel" "$msg" Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/main.c0000644000000000000000000004111412120057602024610 0ustar00usergroup00000000000000/* * Stonith: simple test program for exercising the Stonith API code * * Copyright (C) 2000 Alan Robertson * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #define OPTIONS "c:F:p:t:T:EsnSlLmvhVd" #define EQUAL '=' extern char * optarg; extern int optind, opterr, optopt; static int debug = 0; #define LOG_TERMINAL 0 #define LOG_CLLOG 1 static int log_destination = LOG_TERMINAL; static const char META_TEMPLATE[] = "\n" "\n" "\n" "1.0\n" "\n" "%s\n" "\n" "%s\n" "%s\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "2.0\n" "\n" "\n"; void version(void); void usage(const char * cmd, int exit_status, const char * devtype); void confhelp(const char * cmd, FILE* stream, const char * devtype); void print_stonith_meta(Stonith * stonith_obj, const char *rsc_type); void print_types(void); void print_confignames(Stonith *s); void log_buf(int severity, char *buf); void log_msg(int severity, const char * fmt, ...)G_GNUC_PRINTF(2,3); void trans_log(int priority, const char * fmt, ...)G_GNUC_PRINTF(2,3); static int pil_loglevel_to_syslog_severity[] = { /* Indices: =0, PIL_FATAL=1, PIL_CRIT=2, PIL_WARN=3, PIL_INFO=4, PIL_DEBUG=5 */ LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING, LOG_INFO, LOG_DEBUG }; /* * Note that we don't use the cl_log logging code because the STONITH * command is intended to be shipped without the clplumbing libraries. * * :-( * * The stonith command has so far always been shipped along with * the clplumbing library, so we'll use cl_log * If that ever changes, we'll use something else */ void version() { printf("stonith: %s (%s)\n", GLUE_VERSION, GLUE_BUILD_VERSION); exit(0); } void usage(const char * cmd, int exit_status, const char * devtype) { FILE *stream; stream = exit_status ? stderr : stdout; /* non-NULL devtype indicates help for specific device, so no usage */ if (devtype == NULL) { fprintf(stream, "usage:\n"); fprintf(stream, "\t %s [-svh] " "-L\n" , cmd); fprintf(stream, "\t %s [-svh] " "-t stonith-device-type " "-n\n" , cmd); fprintf(stream, "\t %s [-svh] " "-t stonith-device-type " "-m\n" , cmd); fprintf(stream, "\t %s [-svh] " "-t stonith-device-type " "{-p stonith-device-parameters | " "-F stonith-device-parameters-file | " "-E | " "name=value...} " "[-c count] " "-lS\n" , cmd); fprintf(stream, "\t %s [-svh] " "-t stonith-device-type " "{-p stonith-device-parameters | " "-F stonith-device-parameters-file | " "-E | " "name=value...} " "[-c count] " "-T {reset|on|off} nodename\n" , cmd); fprintf(stream, "\nwhere:\n"); fprintf(stream, "\t-L\tlist supported stonith device types\n"); fprintf(stream, "\t-l\tlist hosts controlled by this stonith device\n"); fprintf(stream, "\t-S\treport stonith device status\n"); fprintf(stream, "\t-s\tsilent\n"); fprintf(stream, "\t-v\tverbose\n"); fprintf(stream, "\t-n\toutput the config names of stonith-device-parameters\n"); fprintf(stream, "\t-m\tdisplay meta-data of the stonith device type\n"); fprintf(stream, "\t-h\tdisplay detailed help message with stonith device description(s)\n"); } if (exit_status == 0) { confhelp(cmd, stream, devtype); } exit(exit_status); } /* Thanks to Lorn Kay for the confhelp code */ void confhelp(const char * cmd, FILE* stream, const char * devtype) { char ** typelist; char ** this; Stonith * s; int devfound = 0; /* non-NULL devtype indicates help for specific device, so no header */ if (devtype == NULL) { fprintf(stream , "\nSTONITH -t device types and" " associated configuration details:\n"); } typelist = stonith_types(); if (typelist == NULL) { fprintf(stderr, "Failed to retrieve list of STONITH modules!\n"); return; } for(this=typelist; *this && !devfound; ++this) { const char * SwitchType = *this; const char * cres; const char * const * pnames; if ((s = stonith_new(SwitchType)) == NULL) { fprintf(stderr, "Invalid STONITH type %s(!)\n" , SwitchType); continue; } if (devtype) { if (strcmp(devtype, SwitchType)) { continue; } else { devfound = 1; } } fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType); if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){ fprintf(stream, "%s\n" , cres); } if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){ fprintf(stream , "For more information see %s\n" , cres); } if (NULL == (pnames = stonith_get_confignames(s))) { continue; } fprintf(stream , "List of valid parameter names for %s STONITH device:\n" , SwitchType); for (;*pnames; ++pnames) { fprintf(stream , "\t%s\n", *pnames); } #ifdef ST_CONFI_INFO_SYNTAX fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n" , SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX)); #else fprintf(stream, "For Config info [-p] syntax" ", give each of the above parameters in order as" "\nthe -p value.\n" "Arguments are separated by white space."); #endif #ifdef ST_CONFI_FILE_SYNTAX fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n" , SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX)); #else fprintf(stream , "\nConfig file [-F] syntax is the same as -p" ", except # at the start of a line" "\ndenotes a comment\n"); #endif stonith_delete(s); s = NULL; } /* Note that the type list can't/shouldn't be freed */ if (devtype && !devfound) { fprintf(stderr, "Invalid device type: '%s'\n", devtype); } } void print_stonith_meta(Stonith * stonith_obj, const char *rsc_type) { const char * meta_param = NULL; const char * meta_longdesc = NULL; const char * meta_shortdesc = NULL; char *xml_meta_longdesc = NULL; char *xml_meta_shortdesc = NULL; static const char * no_parameter_info = ""; meta_longdesc = stonith_get_info(stonith_obj, ST_DEVICEDESCR); if (meta_longdesc == NULL) { fprintf(stderr, "stonithRA plugin: no long description"); meta_longdesc = no_parameter_info; } xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICEID); if (meta_shortdesc == NULL) { fprintf(stderr, "stonithRA plugin: no short description"); meta_shortdesc = no_parameter_info; } xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); meta_param = stonith_get_info(stonith_obj, ST_CONF_XML); if (meta_param == NULL) { fprintf(stderr, "stonithRA plugin: no list of parameters"); meta_param = no_parameter_info; } printf(META_TEMPLATE, rsc_type, xml_meta_longdesc, xml_meta_shortdesc, meta_param); xmlFree(xml_meta_longdesc); xmlFree(xml_meta_shortdesc); } #define MAXNVARG 50 void print_types() { char ** typelist; typelist = stonith_types(); if (typelist == NULL) { log_msg(LOG_ERR, "Could not list Stonith types."); }else{ char ** this; for(this=typelist; *this; ++this) { printf("%s\n", *this); } } } void print_confignames(Stonith *s) { const char * const * names; int i; names = stonith_get_confignames(s); if (names != NULL) { for (i=0; names[i]; ++i) { printf("%s ", names[i]); } } printf("\n"); } void log_buf(int severity, char *buf) { if (severity == LOG_DEBUG && !debug) return; if (log_destination == LOG_TERMINAL) { fprintf(stderr, "%s: %s\n", prio2str(severity),buf); } else { cl_log(severity, "%s", buf); } } void log_msg(int severity, const char * fmt, ...) { va_list ap; char buf[MAXLINE]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf)-1, fmt, ap); va_end(ap); log_buf(severity, buf); } void trans_log(int priority, const char * fmt, ...) { int severity; va_list ap; char buf[MAXLINE]; severity = pil_loglevel_to_syslog_severity[ priority % sizeof (pil_loglevel_to_syslog_severity) ]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf)-1, fmt, ap); va_end(ap); log_buf(severity, buf); } int main(int argc, char** argv) { char * cmdname; int rc; Stonith * s; const char * SwitchType = NULL; const char * optfile = NULL; const char * parameters = NULL; int reset_type = ST_GENERIC_RESET; int verbose = 0; int status = 0; int silent = 0; int listhosts = 0; int listtypes = 0; int listparanames = 0; int params_from_env = 0; int c; int errors = 0; int argcount; StonithNVpair nvargs[MAXNVARG]; int nvcount=0; int j; int count = 1; int help = 0; int metadata = 0; /* The bladehpi stonith plugin makes use of openhpi which is * threaded. The mix of memory allocation without thread * initialization followed by g_thread_init followed by * deallocating that memory results in segfault. Hence the * following G_SLICE setting; see * http://library.gnome.org/devel/glib/stable/glib-Memory-Slices.html#g-slice-alloc */ setenv("G_SLICE", "always-malloc", 1); if ((cmdname = strrchr(argv[0], '/')) == NULL) { cmdname = argv[0]; }else{ ++cmdname; } while ((c = getopt(argc, argv, OPTIONS)) != -1) { switch(c) { case 'c': count = atoi(optarg); if (count < 1) { fprintf(stderr , "bad count [%s]\n" , optarg); usage(cmdname, 1, NULL); } break; case 'd': debug++; break; case 'F': optfile = optarg; break; case 'E': params_from_env = 1; break; case 'h': help++; break; case 'm': metadata++; break; case 'l': ++listhosts; break; case 'L': ++listtypes; break; case 'p': parameters = optarg; break; case 's': ++silent; break; case 'S': ++status; break; case 't': SwitchType = optarg; break; case 'T': if (strcmp(optarg, "on")== 0) { reset_type = ST_POWERON; }else if (strcmp(optarg, "off")== 0) { reset_type = ST_POWEROFF; }else if (strcmp(optarg, "reset")== 0) { reset_type = ST_GENERIC_RESET; }else{ fprintf(stderr , "bad reset type [%s]\n" , optarg); usage(cmdname, 1, NULL); } break; case 'n': ++listparanames; break; case 'v': ++verbose; break; case 'V': version(); break; default: ++errors; break; } } /* if we're invoked by stonithd, log through cl_log */ if (!isatty(fileno(stdin))) { log_destination = LOG_CLLOG; cl_log_set_entity("stonith"); cl_log_enable_stderr(debug?TRUE:FALSE); cl_log_set_facility(HA_LOG_FACILITY); /* Use logd if it's enabled by heartbeat */ cl_inherit_logging_environment(0); } if (help && !errors) { usage(cmdname, 0, SwitchType); } if (debug) { PILpisysSetDebugLevel(debug); setenv("HA_debug","2",0); } if ((optfile && parameters) || (optfile && params_from_env) || (params_from_env && parameters)) { fprintf(stderr , "Please use just one of -F, -p, and -E options\n"); usage(cmdname, 1, NULL); } /* * Process name=value arguments on command line... */ for (;optind < argc; ++optind) { char * eqpos; if ((eqpos=strchr(argv[optind], EQUAL)) == NULL) { break; } if (parameters || optfile || params_from_env) { fprintf(stderr , "Cannot mix name=value and -p, -F, or -E " "style arguments\n"); usage(cmdname, 1, NULL); } if (nvcount >= MAXNVARG) { fprintf(stderr , "Too many name=value style arguments\n"); exit(1); } nvargs[nvcount].s_name = argv[optind]; *eqpos = EOS; nvargs[nvcount].s_value = eqpos+1; nvcount++; } nvargs[nvcount].s_name = NULL; nvargs[nvcount].s_value = NULL; argcount = argc - optind; if (!(argcount == 1 || (argcount < 1 && (status||listhosts||listtypes||listparanames||metadata)))) { ++errors; } if (errors) { usage(cmdname, 1, NULL); } if (listtypes) { print_types(); exit(0); } if (SwitchType == NULL) { log_msg(LOG_ERR,"Must specify device type (-t option)"); usage(cmdname, 1, NULL); } s = stonith_new(SwitchType); if (s == NULL) { log_msg(LOG_ERR,"Invalid device type: '%s'", SwitchType); exit(S_OOPS); } if (debug) { stonith_set_debug(s, debug); } stonith_set_log(s, (PILLogFun)trans_log); if (!listparanames && !metadata && optfile == NULL && parameters == NULL && !params_from_env && nvcount == 0) { const char * const * names; int needs_parms = 1; if (s != NULL && (names = stonith_get_confignames(s)) != NULL && names[0] == NULL) { needs_parms = 0; } if (needs_parms) { fprintf(stderr , "Must specify either -p option, -F option, -E option, or " "name=value style arguments\n"); if (s != NULL) { stonith_delete(s); } usage(cmdname, 1, NULL); } } if (listparanames) { print_confignames(s); stonith_delete(s); s=NULL; exit(0); } if (metadata) { print_stonith_meta(s,SwitchType); stonith_delete(s); s=NULL; exit(0); } /* Old STONITH version 1 stuff... */ if (optfile) { /* Configure the Stonith object from a file */ if ((rc=stonith_set_config_file(s, optfile)) != S_OK) { log_msg(LOG_ERR , "Invalid config file for %s device." , SwitchType); #if 0 log_msg(LOG_INFO, "Config file syntax: %s" , s->s_ops->getinfo(s, ST_CONF_FILE_SYNTAX)); #endif stonith_delete(s); s=NULL; exit(S_BADCONFIG); } }else if (params_from_env) { /* Configure Stonith object from the environment */ StonithNVpair * pairs; if ((pairs = stonith_env_to_NVpair(s)) == NULL) { fprintf(stderr , "Invalid config info for %s device.\n" , SwitchType); stonith_delete(s); s=NULL; exit(1); } if ((rc = stonith_set_config(s, pairs)) != S_OK) { fprintf(stderr , "Invalid config info for %s device\n" , SwitchType); } }else if (parameters) { /* Configure Stonith object from the -p argument */ StonithNVpair * pairs; if ((pairs = stonith1_compat_string_to_NVpair ( s, parameters)) == NULL) { fprintf(stderr , "Invalid STONITH -p parameter [%s]\n" , parameters); stonith_delete(s); s=NULL; exit(1); } if ((rc = stonith_set_config(s, pairs)) != S_OK) { fprintf(stderr , "Invalid config info for %s device\n" , SwitchType); } }else{ /* * Configure STONITH device using cmdline arguments... */ if ((rc = stonith_set_config(s, nvargs)) != S_OK) { const char * const * names; int j; fprintf(stderr , "Invalid config info for %s device\n" , SwitchType); names = stonith_get_confignames(s); if (names != NULL) { fprintf(stderr , "Valid config names are:\n"); for (j=0; names[j]; ++j) { fprintf(stderr , "\t%s\n", names[j]); } } stonith_delete(s); s=NULL; exit(rc); } } for (j=0; j < count; ++j) { rc = S_OK; if (status) { rc = stonith_get_status(s); if (!silent) { if (rc == S_OK) { log_msg((log_destination == LOG_TERMINAL) ? LOG_INFO : LOG_DEBUG, "%s device OK.", SwitchType); }else{ /* Uh-Oh */ log_msg(LOG_ERR, "%s device not accessible." , SwitchType); } } } if (listhosts) { char ** hostlist; hostlist = stonith_get_hostlist(s); if (hostlist == NULL) { log_msg(LOG_ERR, "Could not list hosts for %s." , SwitchType); rc = -1; }else{ char ** this; for(this=hostlist; *this; ++this) { printf("%s\n", *this); } stonith_free_hostlist(hostlist); } } if (optind < argc) { char *nodename; nodename = g_strdup(argv[optind]); g_strdown(nodename); rc = stonith_req_reset(s, reset_type, nodename); g_free(nodename); } } stonith_delete(s); s = NULL; return(rc); } Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/meatclient.c0000644000000000000000000000702412120057602026013 0ustar00usergroup00000000000000/* * Stonith client for Human Operator Stonith device * * Copyright (c) 2001 Gregor Binder * * This program is a rewrite of the "do_meatware" program by * David C. Teigland originally appeared * in the GFS stomith meatware agent. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define OPTIONS "c:w" void usage(const char * cmd); void usage(const char * cmd) { fprintf(stderr, "usage: %s -c node [-w]\n", cmd); exit(S_INVAL); } extern char * optarg; extern int optind, opterr, optopt; int main(int argc, char** argv) { char * cmdname; const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to change this, modify meatware.c as well */ char * opthost = NULL; int clearhost = 0; int c, argcount, waitmode; int errors = 0; if ((cmdname = strrchr(argv[0], '/')) == NULL) { cmdname = argv[0]; }else{ ++cmdname; } while ((c = getopt(argc, argv, OPTIONS)) != -1) { switch(c) { case 'c': opthost = optarg; ++clearhost; break; case 'w': ++waitmode; break; default: ++errors; break; } } argcount = argc - optind; if (!(argcount == 0) || !opthost) { errors++; } if (errors) { usage(cmdname); } g_strdown(opthost); if (clearhost) { int rc, fd; char resp[3]; char line[256]; char meatpipe[256]; gboolean waited=FALSE; snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, opthost); while(1) { fd = open(meatpipe, O_WRONLY | O_NONBLOCK); if (fd >= 0) break; if (!waitmode || (errno != ENOENT && errno != ENXIO)) { if (waited) printf("\n"); snprintf(line, sizeof(line) , "Meatware_IPC failed: %s", meatpipe); perror(line); exit(S_BADHOST); } printf("."); fflush(stdout); waited=TRUE; sleep(1); } if (waited) printf("\n"); printf("\nWARNING!\n\n" "If node \"%s\" has not been manually power-cycled or " "disconnected from all shared resources and networks, " "data on shared disks may become corrupted and " "migrated services might not work as expected.\n" "Please verify that the name or address above " "corresponds to the node you just rebooted.\n\n" "PROCEED? [yN] ", opthost); rc = scanf("%s", resp); if (rc == 0 || rc == EOF || tolower(resp[0] != 'y')) { printf("Meatware_client: operation canceled.\n"); exit(S_INVAL); } sprintf(line, "meatware reply %s", opthost); rc = write(fd, line, 256); if (rc < 0) { sprintf(line, "Meatware_IPC failed: %s", meatpipe); perror(line); exit(S_OOPS); } printf("Meatware_client: reset confirmed.\n"); } exit(S_OK); } Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/st_ttylock.c0000644000000000000000000001456112120057602026071 0ustar00usergroup00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include /* * The following information is from the Filesystem Hierarchy Standard * version 2.1 dated 12 April, 2000. * * 5.6 /var/lock : Lock files * Lock files should be stored within the /var/lock directory structure. * Device lock files, such as the serial device lock files that were originally * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in * /var/lock. The naming convention which must be used is LCK.. followed by * the base name of the device file. For example, to lock /dev/cua0 the file * LCK..cua0 would be created. * * The format used for device lock files must be the HDB UUCP lock file format. * The HDB format is to store the process identifier (PID) as a ten byte * ASCII decimal number, with a trailing newline. For example, if process 1230 * holds a lock file, it would contain the eleven characters: space, space, * space, space, space, space, one, two, three, zero, and newline. * Then, anything wishing to use /dev/cua0 can read the lock file and act * accordingly (all locks in /var/lock should be world-readable). * * * PERMISSIONS NOTE: * Different linux distributions set the mode of the lock directory differently * Any process which wants to create lock files must have write permissions * on HA_VARLOCKDIR (probably /var/lock). For things like the heartbeat API * code, this may mean allowing the uid of the processes that use this API * to join group uucp, or making the binaries setgid to uucp. */ #define DEVDIR "/dev/" #define DEVLEN (sizeof(DEVDIR)-1) static void raw_device (const char *dev, char *dest_name, size_t size); static int DoLock(const char * prefix, const char *lockname); static int DoUnlock(const char * prefix, const char *lockname); /* The code in this file originally written by Guenther Thomsen */ /* Somewhat mangled by Alan Robertson */ /* * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL) * serial_device has to be _the complete path_, i.e. including '/dev/' to the * special file, which denotes the tty to lock -tho * return 0 on success, * -1 if device is locked (lockfile exists and isn't stale), * -2 for temporarily failure, try again, * other negative value, if something unexpected happend (failure anyway) */ static void raw_device (const char *serial_device, char *dest_name, size_t size) { char* dp = dest_name; const char* sp = serial_device+DEVLEN; const char* dpend = dp + size - 1; while (*sp != '\0' && dp < dpend) { if (isalnum((unsigned int)*sp)) *dp++ = *sp; sp++; } *dp = EOS; } int st_ttylock(const char *serial_device) { char rawname[64]; if (serial_device == NULL) { errno = EFAULT; return -3; } raw_device (serial_device, rawname, sizeof(rawname)); return(DoLock("LCK..", rawname)); } /* * Unlock a tty (remove its lockfile) * do we need to check, if its (still) ours? No, IMHO, if someone else * locked our line, it's his fault -tho * returns 0 on success * <0 if some failure occured */ int st_ttyunlock(const char *serial_device) { char rawname[64]; if (serial_device == NULL) { errno = EFAULT; return -3; } raw_device (serial_device, rawname, sizeof(rawname)); return(DoUnlock("LCK..", rawname)); } /* This is what the FHS standard specifies for the size of our lock file */ #define LOCKSTRLEN 11 static int DoLock(const char * prefix, const char *lockname) { char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1]; int fd; long pid, mypid; int rc; struct stat sbuf; mypid = (unsigned long) getpid(); snprintf(lf_name, sizeof(lf_name), "%s/%s%s" , HA_VARLOCKDIR, prefix, lockname); snprintf(tf_name, sizeof(tf_name), "%s/tmp%lu-%s" , HA_VARLOCKDIR, mypid, lockname); if ((fd = open(lf_name, O_RDONLY)) >= 0) { if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) { sleep(1); /* if someone was about to create one, * give'm a sec to do so * Though if they follow our protocol, * this won't happen. They should really * put the pid in, then link, not the * other way around. */ } if (read(fd, buf, sizeof(buf)) < 1) { /* lockfile empty -> rm it and go on */; } else { if (sscanf(buf, "%lu", &pid) < 1) { /* lockfile screwed up -> rm it and go on */ } else { if (pid > 1 && ((long)getpid() != pid) && ((CL_KILL((pid_t)pid, 0) >= 0) || errno != ESRCH)) { /* tty is locked by existing (not * necessarily running) process * -> give up */ close(fd); return -1; } else { /* stale lockfile -> rm it and go on */ } } } unlink(lf_name); } if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) { /* Hmmh, why did we fail? Anyway, nothing we can do about it */ return -3; } /* Slight overkill with the %*d format ;-) */ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid); if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) { /* Again, nothing we can do about this */ return -3; } close(fd); switch (link(tf_name, lf_name)) { case 0: if (stat(tf_name, &sbuf) < 0) { /* something weird happened */ rc = -3; break; } if (sbuf.st_nlink < 2) { /* somehow, it didn't get through - NFS trouble? */ rc = -2; break; } rc = 0; break; case EEXIST: rc = -1; break; default: rc = -3; } unlink(tf_name); return rc; } static int DoUnlock(const char * prefix, const char *lockname) { char lf_name[256]; snprintf(lf_name, sizeof(lf_name), "%s/%s%s", HA_VARLOCKDIR , prefix, lockname); return unlink(lf_name); } Reusable-Cluster-Components-glue--3cff550e1084/lib/stonith/stonith.c0000644000000000000000000003254512120057602025364 0ustar00usergroup00000000000000/* * Stonith API infrastructure. * * Copyright (c) 2000 Alan Robertson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #define ENABLE_PIL_DEFS_PRIVATE #include #include #include #include #define MALLOC StonithPIsys->imports->alloc #ifdef MALLOCT # undef MALLOCT #endif #define MALLOCT(t) (t*)(MALLOC(sizeof(t))) #define REALLOC StonithPIsys->imports->mrealloc #define STRDUP StonithPIsys->imports->mstrdup #define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;} #define LOG(args...) PILCallLog(StonithPIsys->imports->log, args) #define EXTPINAME_S "external" #define RHCSPINAME_S "rhcs" PILPluginUniv* StonithPIsys = NULL; static GHashTable* Splugins = NULL; static int init_pluginsys(void); extern StonithImports stonithimports; static PILGenericIfMgmtRqst Reqs[] = { {STONITH_TYPE_S, &Splugins, &stonithimports, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; void PILpisysSetDebugLevel(int); /* Initialize the plugin system... */ static int init_pluginsys(void) { if (StonithPIsys) { return TRUE; } /* PILpisysSetDebugLevel(10); */ StonithPIsys = NewPILPluginUniv(STONITH_MODULES); if (StonithPIsys) { int rc = PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs); if (rc != PIL_OK) { fprintf(stderr, "generic plugin load failed: %d\n", rc); DelPILPluginUniv(StonithPIsys); StonithPIsys = NULL; } /*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/ }else{ fprintf(stderr, "pi univ creation failed\n"); } return StonithPIsys != NULL; } /* * Create a new Stonith object of the requested type. */ Stonith * stonith_new(const char * type) { StonithPlugin * sp = NULL; struct stonith_ops* ops = NULL; char * key; char * subplugin; char * typecopy; if (!init_pluginsys()) { return NULL; } if ((typecopy = STRDUP(type)) == NULL) { return NULL; } if (((subplugin = strchr(typecopy, '/')) != NULL) && (strncmp(EXTPINAME_S, typecopy, strlen(EXTPINAME_S)) == 0 || strncmp(RHCSPINAME_S, typecopy, strlen(RHCSPINAME_S)) == 0)) { *subplugin++ = 0; /* make two strings */ } /* Look and see if it's already loaded... */ if (g_hash_table_lookup_extended(Splugins, typecopy , (gpointer)&key, (gpointer)&ops)) { /* Yes! Increment reference count */ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, typecopy, 1); }else{ /* No. Try and load it... */ if (PILLoadPlugin(StonithPIsys, STONITH_TYPE_S, typecopy, NULL) != PIL_OK) { FREE(typecopy); return NULL; } /* Look up the plugin in the Splugins table */ if (!g_hash_table_lookup_extended(Splugins, typecopy , (void*)&key, (void*)&ops)) { /* OOPS! didn't find it(!?!)... */ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S , typecopy, -1); FREE(typecopy); return NULL; } } if (ops != NULL) { sp = ops->new((const char *)(subplugin)); if (sp != NULL) { sp->s.stype = STRDUP(typecopy); } } FREE(typecopy); return sp ? (&sp->s) : NULL; } static int qsort_string_cmp(const void *a, const void *b) { return(strcmp(*(const char * const *)a, *(const char * const *)b)); } /* * Return list of STONITH types valid in stonith_new() */ static char ** get_plugin_list(const char *pltype) { char ** typelist = NULL; const char * const *extPI; const char * const *p; int numextPI, i; Stonith * ext; /* let the external plugin return a list */ if ((ext = stonith_new(pltype)) == NULL) { LOG(PIL_CRIT, "Cannot create new external " "plugin object"); return NULL; } if ((extPI = stonith_get_confignames(ext)) == NULL) { /* don't complain if rhcs plugins are not installed */ if (strcmp(pltype, "rhcs")) LOG(PIL_INFO, "Cannot get %s plugin subplugins", pltype); stonith_delete(ext); return NULL; } /* count the external plugins */ for (numextPI = 0, p = extPI; *p; p++, numextPI++); typelist = (char **) MALLOC((numextPI+1)*sizeof(char *)); if (typelist == NULL) { LOG(PIL_CRIT, "Out of memory"); stonith_delete(ext); return NULL; } memset(typelist, 0, (numextPI + 1)*sizeof(char *)); /* copy external plugins */ for (i = 0; i < numextPI; i++) { int len = strlen(pltype) + strlen(extPI[i]) + 2; typelist[i] = MALLOC(len); if (typelist[i] == NULL) { LOG(PIL_CRIT, "Out of memory"); stonith_delete(ext); goto err; } snprintf(typelist[i], len, "%s/%s" , pltype, extPI[i]); } stonith_delete(ext); /* sort the list of plugin names */ qsort(typelist, numextPI, sizeof(char *), qsort_string_cmp); return typelist; err: stonith_free_hostlist(typelist); return NULL; } char ** stonith_types(void) { int i, j, cur=0, rl_size, sub_pl = 0; static char ** rl = NULL; char ** new_list, **sub_list = NULL; if (!init_pluginsys()) { return NULL; } new_list = PILListPlugins(StonithPIsys, STONITH_TYPE_S, NULL); if (new_list == NULL) { return NULL; } for (i=0; new_list[i]; ++i) ; /* count */ rl_size = i+1; rl = (char**)MALLOC(rl_size * sizeof(char *)); if (rl == NULL) { LOG(PIL_CRIT, "Out of memory"); goto types_exit; } for (i=0; new_list[i]; ++i) { /* look for 'external' and 'rhcs' plugins */ if (strcmp(new_list[i], EXTPINAME_S) == 0) { sub_list = get_plugin_list(EXTPINAME_S); sub_pl = 1; } else if (strcmp(new_list[i], RHCSPINAME_S) == 0) { sub_list = get_plugin_list(RHCSPINAME_S); sub_pl = 1; } if (sub_pl) { if (sub_list) { for (j=0; sub_list[j]; ++j) ; /* count */ rl_size += j; rl = (char**)REALLOC(rl, rl_size*sizeof(char *)); for (j=0; sub_list[j]; ++j) { rl[cur++] = sub_list[j]; } FREE(sub_list); sub_list = NULL; } sub_pl = 0; } else { rl[cur] = STRDUP(new_list[i]); if (rl[cur] == NULL) { LOG(PIL_CRIT, "Out of memory"); goto types_exit_mem; } cur++; } } rl[cur] = NULL; goto types_exit; types_exit_mem: stonith_free_hostlist(rl); rl = NULL; types_exit: PILFreePluginList(new_list); return rl; } /* Destroy the STONITH object... */ void stonith_delete(Stonith *s) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops) { char * st = sp->s.stype; sp->s_ops->destroy(sp); PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, st, -1); /* destroy should not free it */ FREE(st); } } const char * const * stonith_get_confignames(Stonith* s) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops) { return sp->s_ops->get_confignames(sp); } return NULL; } const char* stonith_get_info(Stonith* s, int infotype) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops) { return sp->s_ops->get_info(sp, infotype); } return NULL; } void stonith_set_debug (Stonith* s, int debuglevel) { StonithPlugin* sp = (StonithPlugin*)s; if (StonithPIsys == NULL) { return; } PILSetDebugLevel(StonithPIsys, STONITH_TYPE_S, sp->s.stype, debuglevel); } void stonith_set_log(Stonith* s, PILLogFun logfun) { if (StonithPIsys == NULL) { return; } PilPluginUnivSetLog(StonithPIsys, logfun); } int stonith_set_config(Stonith* s, StonithNVpair* list) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops) { int rc = sp->s_ops->set_config(sp, list); if (rc == S_OK) { sp->isconfigured = TRUE; } return rc; } return S_INVAL; } /* * FIXME: We really ought to support files with name=value type syntax * on each line... * */ int stonith_set_config_file(Stonith* s, const char * configname) { FILE * cfgfile; char line[1024]; if ((cfgfile = fopen(configname, "r")) == NULL) { LOG(PIL_CRIT, "Cannot open %s", configname); return(S_BADCONFIG); } while (fgets(line, sizeof(line), cfgfile) != NULL){ int len; if (*line == '#' || *line == '\n' || *line == EOS) { continue; } /*remove the new line in the end*/ len = strnlen(line, sizeof(line)-1); if (line[len-1] == '\n'){ line[len-1] = '\0'; }else{ line[len] = '\0'; } fclose(cfgfile); return stonith_set_config_info(s, line); } fclose(cfgfile); return S_BADCONFIG; } int stonith_set_config_info(Stonith* s, const char * info) { StonithNVpair* cinfo; int rc; cinfo = stonith1_compat_string_to_NVpair(s, info); if (cinfo == NULL) { return S_BADCONFIG; } rc = stonith_set_config(s, cinfo); free_NVpair(cinfo); cinfo = NULL; return rc; } char** stonith_get_hostlist(Stonith* s) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops && sp->isconfigured) { return sp->s_ops->get_hostlist(sp); } return NULL; } void stonith_free_hostlist(char** hostlist) { char ** here; for (here=hostlist; *here; ++here) { FREE(*here); } FREE(hostlist); } int stonith_get_status(Stonith* s) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops && sp->isconfigured) { return sp->s_ops->get_status(sp); } return S_INVAL; } int stonith_req_reset(Stonith* s, int operation, const char* node) { StonithPlugin* sp = (StonithPlugin*)s; if (sp && sp->s_ops && sp->isconfigured) { char* nodecopy = STRDUP(node); int rc; if (nodecopy == NULL) { return S_OOPS; } g_strdown(nodecopy); rc = sp->s_ops->req_reset(sp, operation, nodecopy); FREE(nodecopy); return rc; } return S_INVAL; } /* Stonith 1 compatibility: Convert a string to an NVpair set */ StonithNVpair* stonith1_compat_string_to_NVpair(Stonith* s, const char * str) { /* We make some assumptions that the order of parameters in the * result from stonith_get_confignames() matches that which * was required from a Stonith1 module. * Everything after the last delimiter is passed along as part of * the final argument - white space and all... */ const char * const * config_names; int n_names; int j; const char * delims = " \t\n\r\f"; StonithNVpair* ret; if ((config_names = stonith_get_confignames(s)) == NULL) { return NULL; } for (n_names=0; config_names[n_names] != NULL; ++n_names) { /* Just count */; } ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair))); if (ret == NULL) { return NULL; } memset(ret, 0, (n_names+1)*sizeof(StonithNVpair)); for (j=0; j < n_names; ++j) { size_t len; if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) { goto freeandexit; } ret[j].s_value = NULL; str += strspn(str, delims); if (*str == EOS) { goto freeandexit; } if (j == (n_names -1)) { len = strlen(str); }else{ len = strcspn(str, delims); } if ((ret[j].s_value = MALLOC((len+1)*sizeof(char))) == NULL) { goto freeandexit; } memcpy(ret[j].s_value, str, len); ret[j].s_value[len] = EOS; str += len; } ret[j].s_name = NULL; return ret; freeandexit: free_NVpair(ret); ret = NULL; return NULL; } StonithNVpair* stonith_env_to_NVpair(Stonith* s) { /* Read the config names values from the environment */ const char * const * config_names; int n_names; int j; StonithNVpair* ret; if ((config_names = stonith_get_confignames(s)) == NULL) { return NULL; } for (n_names=0; config_names[n_names] != NULL; ++n_names) { /* Just count */; } ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair))); if (ret == NULL) { return NULL; } memset(ret, 0, (n_names+1)*sizeof(StonithNVpair)); for (j=0; j < n_names; ++j) { char *env_value; if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) { goto freeandexit; } env_value = getenv(config_names[j]); if (env_value) { if ((ret[j].s_value = STRDUP(env_value)) == NULL) { goto freeandexit; } } else { ret[j].s_value = NULL; } } ret[j].s_name = NULL; return ret; freeandexit: free_NVpair(ret); ret = NULL; return NULL; } static int NVcur = -1; static int NVmax = -1; static gboolean NVerr = FALSE; static void stonith_walk_ghash(gpointer key, gpointer value, gpointer user_data) { StonithNVpair* u = user_data; if (NVcur <= NVmax && !NVerr) { u[NVcur].s_name = STRDUP(key); u[NVcur].s_value = STRDUP(value); if (u[NVcur].s_name == NULL || u[NVcur].s_value == NULL) { /* Memory allocation error */ NVerr = TRUE; return; } ++NVcur; }else{ NVerr = TRUE; } } StonithNVpair* stonith_ghash_to_NVpair(GHashTable* stringtable) { int hsize = g_hash_table_size(stringtable); StonithNVpair* ret; if ((ret = (StonithNVpair*)MALLOC(sizeof(StonithNVpair)*(hsize+1))) == NULL) { return NULL; } NVmax = hsize; NVcur = 0; ret[hsize].s_name = NULL; ret[hsize].s_value = NULL; g_hash_table_foreach(stringtable, stonith_walk_ghash, ret); NVmax = NVcur = -1; if (NVerr) { free_NVpair(ret); ret = NULL; } return ret; } void free_NVpair(StonithNVpair* nv) { StonithNVpair* this; if (nv == NULL) { return; } for (this=nv; this->s_name; ++this) { FREE(this->s_name); if (this->s_value) { FREE(this->s_value); } } FREE(nv); } Reusable-Cluster-Components-glue--3cff550e1084/logd/Makefile.am0000644000000000000000000000324412120057602024245 0ustar00usergroup00000000000000# # hbclient library: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # Copyright (C) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl halibdir = $(libdir)/@HB_PKG@ ha_sbindir = $(sbindir) LIBRT = @LIBRT@ AM_CFLAGS = @CFLAGS@ initddir = @INITDIR@ ## binary progs ha_sbin_PROGRAMS = ha_logger halib_PROGRAMS = ha_logd logtest ha_logd_SOURCES = ha_logd.c ha_logd_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/clplumbing/libplumbgpl.la # $(top_builddir)/lib/apphb/libapphb.la ha_logger_SOURCES = ha_logger.c ha_logger_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la logtest_SOURCES = logtest.c logtest_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la initd_SCRIPTS = logd Reusable-Cluster-Components-glue--3cff550e1084/logd/ha_logd.c0000644000000000000000000005673512120057602023767 0ustar00usergroup00000000000000/* * ha_logd.c logging daemon * * Copyright (C) 2004 Guochun Shi * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*two processes involved 1. parent process which reads messages from all client channels and writes them to the child process 2. the child process which reads messages from the parent process through IPC and writes them to syslog/disk I call the parent process READ process, and the child process WRITE one, for convenience. */ #define DEFAULT_CFG_FILE HA_SYSCONFDIR "/logd.cf" #define LOGD_PIDFILE HA_VARRUNDIR "/logd.pid" #define FD_STDIN 0 #define FD_STDOUT 1 #define FD_STDERR 2 #define WRITE_PROC_CHAN 0 #define READ_PROC_CHAN 1 #define LOGD_QUEUE_LEN 128 #define EOS '\0' #define nullchk(a) ((a) ? (a) : "") static const int logd_keepalive_ms = 1000; static const int logd_warntime_ms = 5000; static const int logd_deadtime_ms = 10000; static gboolean verbose = FALSE; static pid_t write_process_pid; static IPC_Channel *chanspair[2]; static gboolean stop_reading = FALSE; static gboolean needs_shutdown = FALSE; static struct { char debugfile[MAXLINE]; char logfile[MAXLINE]; char entity[MAXENTITY]; char syslogprefix[MAXENTITY]; int log_facility; mode_t logmode; gboolean syslogfmtmsgs; } logd_config = { .debugfile = "", .logfile = "", .entity = "logd", .syslogprefix = "", .log_facility = HA_LOG_FACILITY, .logmode = 0644, .syslogfmtmsgs = FALSE }; static void logd_log(const char * fmt, ...) G_GNUC_PRINTF(1,2); static int set_debugfile(const char* option); static int set_logfile(const char* option); static int set_facility(const char * value); static int set_entity(const char * option); static int set_syslogprefix(const char * option); static int set_sendqlen(const char * option); static int set_recvqlen(const char * option); static int set_logmode(const char * option); static int set_syslogfmtmsgs(const char * option); static char* cmdname = NULL; static struct directive { const char* name; int (*add_func)(const char*); } Directives[] = { {"debugfile", set_debugfile}, {"logfile", set_logfile}, {"logfacility", set_facility}, {"entity", set_entity}, {"syslogprefix",set_syslogprefix}, {"sendqlen", set_sendqlen}, {"recvqlen", set_recvqlen}, {"logmode", set_logmode}, {"syslogmsgfmt",set_syslogfmtmsgs} }; static void logd_log( const char * fmt, ...) { char buf[MAXLINE]; va_list ap; buf[MAXLINE-1] = EOS; va_start(ap, fmt); vsnprintf(buf, sizeof(buf)-1, fmt, ap); va_end(ap); fprintf(stderr, "%s", buf); return; } static int set_debugfile(const char* option) { if (!option){ logd_config.debugfile[0] = EOS; return FALSE; } cl_log(LOG_INFO, "setting debug file to %s", option); strncpy(logd_config.debugfile, option, MAXLINE); return TRUE; } static int set_logfile(const char* option) { if (!option){ logd_config.logfile[0] = EOS; return FALSE; } cl_log(LOG_INFO, "setting log file to %s", option); strncpy(logd_config.logfile, option, MAXLINE); return TRUE; } /* set syslog facility config variable */ static int set_facility(const char * value) { int i; i = cl_syslogfac_str2int(value); if (i >= 0) { cl_log(LOG_INFO, "setting log facility to %s", value); logd_config.log_facility = i; return(TRUE); } else { return(FALSE); } } static int set_entity(const char * option) { if (!option){ logd_config.entity[0] = EOS; return FALSE; } strncpy(logd_config.entity, option, MAXENTITY); logd_config.entity[MAXENTITY-1] = '\0'; if (strlen(option) >= MAXENTITY) cl_log(LOG_WARNING, "setting entity to %s (truncated from %s)", logd_config.entity, option); else cl_log(LOG_INFO, "setting entity to %s", logd_config.entity); return TRUE; } static int set_syslogprefix(const char * option) { if (!option){ logd_config.syslogprefix[0] = EOS; return FALSE; } strncpy(logd_config.syslogprefix, option, MAXENTITY); logd_config.syslogprefix[MAXENTITY-1] = '\0'; if (strlen(option) >= MAXENTITY) cl_log(LOG_WARNING, "setting syslogprefix to %s (truncated from %s)", logd_config.syslogprefix, option); else cl_log(LOG_INFO, "setting syslogprefix to %s", logd_config.syslogprefix); return TRUE; } static int set_sendqlen(const char * option) { int length; if (!option){ cl_log(LOG_ERR, "NULL send queue length"); return FALSE; } length = atoi(option); if (length < 0){ cl_log(LOG_ERR, "negative send queue length"); return FALSE; } cl_log(LOG_INFO, "setting send queue length to %d", length); chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN], length); return TRUE; } static int set_recvqlen(const char * option) { int length; if (!option){ cl_log(LOG_ERR, "NULL recv queue length"); return FALSE; } length = atoi(option); if (length < 0){ cl_log(LOG_ERR, "negative recv queue length"); return FALSE; } cl_log(LOG_INFO, "setting recv queue length to %d", length); chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN], length); return TRUE; } static int set_logmode(const char * option) { unsigned long mode; char * endptr; if (!option){ cl_log(LOG_ERR, "NULL logmode parameter"); return FALSE; } mode = strtoul(option, &endptr, 8); if (*endptr != EOS) { cl_log(LOG_ERR, "Invalid log mode [%s]", option); return FALSE; } if (*option != '0') { /* Whine if mode doesn't start with '0' */ cl_log(LOG_WARNING, "Log mode [%s] assumed to be octal" , option); } logd_config.logmode = (mode_t)mode; return TRUE; } static int set_syslogfmtmsgs(const char * option) { gboolean dosyslogfmt; if (cl_str_to_boolean(option, &dosyslogfmt) == HA_OK) { cl_log_enable_syslog_filefmt(dosyslogfmt); }else{ return FALSE; } return TRUE; } typedef struct { char app_name[MAXENTITY]; pid_t pid; gid_t gid; uid_t uid; IPC_Channel* chan; IPC_Channel* logchan; GCHSource* g_src; }ha_logd_client_t; static GList* logd_client_list = NULL; static IPC_Message* getIPCmsg(IPC_Channel* ch) { int rc; IPC_Message* ipcmsg; /* FIXME: Should we block here?? */ rc = ch->ops->waitin(ch); switch(rc) { default: case IPC_FAIL: cl_log(LOG_ERR, "getIPCmsg: waitin failure\n"); return NULL; case IPC_BROKEN: sleep(1); return NULL; case IPC_INTR: return NULL; case IPC_OK: break; } ipcmsg = NULL; rc = ch->ops->recv(ch, &ipcmsg); if (rc != IPC_OK) { return NULL; } return ipcmsg; } /* Flow control all clients off */ static void logd_suspend_clients(IPC_Channel* notused1, gpointer notused2) { GList * gl; stop_reading = TRUE; for (gl=g_list_first(logd_client_list); gl != NULL ; gl = g_list_next(gl)) { ha_logd_client_t* client = gl->data; if (client && client->g_src) { G_main_IPC_Channel_pause(client->g_src); }else if (client) { cl_log(LOG_ERR, "Could not suspend client [%s] pid %d" , nullchk(client->app_name), client->pid); }else{ cl_log(LOG_ERR, "%s: Could not suspend NULL client", __FUNCTION__); } } } /* Resume input from clients - Flow control all clients back on */ static void logd_resume_clients(IPC_Channel* notused1, gpointer notused2) { GList * gl; stop_reading = FALSE; for (gl=g_list_first(logd_client_list); gl != NULL ; gl = g_list_next(gl)) { ha_logd_client_t* client = gl->data; if (client && client->g_src) { G_main_IPC_Channel_resume(client->g_src); }else if (client) { cl_log(LOG_ERR, "Could not resume client [%s] pid %d" , nullchk(client->app_name), client->pid); }else{ cl_log(LOG_ERR, "%s: Could not suspend NULL client", __FUNCTION__); } } } static gboolean on_receive_cmd (IPC_Channel* ch, gpointer user_data) { IPC_Message* ipcmsg; ha_logd_client_t* client = (ha_logd_client_t*)user_data; IPC_Channel* logchan= client->logchan; if (!ch->ops->is_message_pending(ch)) { goto getout; } ipcmsg = getIPCmsg(ch); if (ipcmsg == NULL){ if (IPC_ISRCONN(ch)) { cl_log(LOG_ERR, "%s: read error on connected channel [%s:%d]" , __FUNCTION__, client->app_name, client->pid); } return FALSE; } if( ipcmsg->msg_body && ipcmsg->msg_len > 0 ){ if (client->app_name[0] == '\0'){ LogDaemonMsgHdr* logmsghdr; logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body; strncpy(client->app_name, logmsghdr->entity, MAXENTITY); } if (!IPC_ISWCONN(logchan)){ cl_log(LOG_ERR , "%s: channel to write process disconnected" , __FUNCTION__); return FALSE; } if (logchan->ops->send(logchan, ipcmsg) != IPC_OK){ cl_log(LOG_ERR , "%s: forwarding msg from [%s:%d] to" " write process failed" , __FUNCTION__ , client->app_name, client->pid); cl_log(LOG_ERR, "queue too small? (max=%ld, current len =%ld)", (long)logchan->send_queue->max_qlen, (long)logchan->send_queue->current_qlen); return TRUE; } }else { cl_log(LOG_ERR, "on_receive_cmd:" " invalid ipcmsg\n"); } getout: return TRUE; } static void on_remove_client (gpointer user_data) { logd_client_list = g_list_remove(logd_client_list, user_data); if (user_data){ free(user_data); } return; } /* *GLoop Message Handlers */ static gboolean on_connect_cmd (IPC_Channel* ch, gpointer user_data) { ha_logd_client_t* client = NULL; /* check paremeters */ if (NULL == ch) { cl_log(LOG_ERR, "on_connect_cmd: channel is null"); return TRUE; } /* create new client */ if (NULL == (client = malloc(sizeof(ha_logd_client_t)))) { return FALSE; } memset(client, 0, sizeof(ha_logd_client_t)); client->pid = ch->farside_pid; client->chan = ch; client->logchan = (IPC_Channel*)user_data; client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, ch, FALSE, on_receive_cmd, (gpointer)client, on_remove_client); if (client->g_src == NULL){ cl_log(LOG_ERR, "add the client to main loop failed"); free(client); return TRUE; } if (stop_reading){ G_main_IPC_Channel_pause(client->g_src); } logd_client_list = g_list_append(logd_client_list, client); return TRUE; } static void logd_make_daemon(gboolean daemonize) { long pid; if (daemonize) { if (daemon(0,0)) { fprintf(stderr, "%s: could not start daemon\n" , cmdname); perror("fork"); exit(LSB_EXIT_GENERIC); } } if (cl_lock_pidfile(LOGD_PIDFILE) < 0 ){ pid = cl_read_pidfile(LOGD_PIDFILE); if (pid > 0) fprintf(stderr, "%s: already running [pid %ld].\n", cmdname, pid); else fprintf(stderr, "%s: problem creating pid file %s\n", cmdname, LOGD_PIDFILE); exit(LSB_EXIT_OK); } if (daemonize || !verbose){ cl_log_enable_stderr(FALSE); } if (daemonize){ mode_t mask; /* * Some sample umask calculations: * * logmode = 0644 * * (~0644)&0777 = 0133 * (0133 & ~0111) = 0022 * => umask will be 022 (the expected result) * * logmode = 0600 * (~0600)&0777 = 0177 * (0177 & ~0111) = 0066 */ mask = (mode_t)(((~logd_config.logmode) & 0777) & (~0111)); umask(mask); } } static void logd_stop(void) { long running_logd_pid = cl_read_pidfile(LOGD_PIDFILE); int err; if (running_logd_pid < 0) { fprintf(stderr, "ha_logd already stopped.\n"); cl_log(LOG_INFO, "ha_logd already stopped."); exit(LSB_EXIT_OK); } cl_log(LOG_DEBUG, "Stopping ha_logd with pid %ld", running_logd_pid); if (kill((pid_t)running_logd_pid, SIGTERM) >= 0) { /* Wait for the running logd to die */ cl_log(LOG_INFO, "Waiting for pid=%ld to exit", running_logd_pid); alarm(0); do { sleep(1); }while (kill((pid_t)running_logd_pid, 0) >= 0); } err = errno; if(errno == ESRCH) { cl_log(LOG_INFO, "Pid %ld exited", running_logd_pid); exit(LSB_EXIT_OK); } else { cl_perror("Pid %ld not killed", running_logd_pid); exit((err == EPERM || err == EACCES) ? LSB_EXIT_EPERM : LSB_EXIT_GENERIC); } } static int get_dir_index(const char* directive) { int j; for(j=0; j < DIMOF(Directives); j++){ if (0 == strcasecmp(directive, Directives[j].name)){ return j; } } return -1; } /* Adapted from parse_config in config.c */ static gboolean parse_config(const char* cfgfile) { FILE* f; char buf[MAXLINE]; char* bp; char* cp; char directive[MAXLINE]; int dirlength; int optionlength; char option[MAXLINE]; int dir_index; gboolean ret = TRUE; if ((f = fopen(cfgfile, "r")) == NULL){ cl_perror("Cannot open config file [%s]", cfgfile); return(FALSE); } while(fgets(buf, MAXLINE, f) != NULL){ bp = buf; /* Skip over white space*/ bp += strspn(bp, " \t\n\r\f"); /* comments */ if ((cp = strchr(bp, '#')) != NULL){ *cp = EOS; } if (*bp == EOS){ continue; } dirlength = strcspn(bp, " \t\n\f\r"); strncpy(directive, bp, dirlength); directive[dirlength] = EOS; if ((dir_index = get_dir_index(directive)) == -1){ fprintf(stderr, "Illegal directive [%s] in %s\n" , directive, cfgfile); ret = FALSE; continue; } bp += dirlength; /* skip delimiters */ bp += strspn(bp, " ,\t\n\f\r"); /* Set option */ optionlength = strcspn(bp, " ,\t\n\f\r"); strncpy(option, bp, optionlength); option[optionlength] = EOS; if (!(*Directives[dir_index].add_func)(option)) { ret = FALSE; } }/*while*/ fclose(f); return ret; } static gboolean logd_term_action(int sig, gpointer userdata) { GList *log_iter = logd_client_list; GMainLoop *mainloop = (GMainLoop*)userdata; ha_logd_client_t *client = NULL; cl_log(LOG_DEBUG, "logd_term_action: received SIGTERM"); if (mainloop == NULL){ cl_log(LOG_ERR, "logd_term_action: invalid arguments"); return FALSE; } stop_reading = TRUE; while(log_iter != NULL) { client = log_iter->data; log_iter = log_iter->next; cl_log(LOG_DEBUG, "logd_term_action:" " waiting for %d messages to be read for process %s", (int)client->logchan->send_queue->current_qlen, client->app_name); client->logchan->ops->waitout(client->logchan); } cl_log(LOG_DEBUG, "logd_term_action" ": waiting for %d messages to be read by write process" , (int)chanspair[WRITE_PROC_CHAN]->send_queue->current_qlen); chanspair[WRITE_PROC_CHAN]->ops->waitout(chanspair[WRITE_PROC_CHAN]); cl_log(LOG_DEBUG, "logd_term_action: sending SIGTERM to write process"); if (CL_KILL(write_process_pid, SIGTERM) >= 0){ pid_t pid; pid = wait4(write_process_pid, NULL, 0, NULL); if (pid < 0){ cl_log(LOG_ERR, "wait4 for write process failed"); } } g_main_quit(mainloop); return TRUE; } /* * Handle SIGHUP to re-open log files */ static gboolean logd_hup_action(int sig, gpointer userdata) { cl_log_close_log_files(); if (write_process_pid) /* do we want to propagate the HUP, * or do we assume that it was a killall anyways? */ CL_KILL(write_process_pid, SIGHUP); else cl_log(LOG_INFO, "SIGHUP received, re-opened log files"); return TRUE; } static void read_msg_process(IPC_Channel* chan) { GHashTable* conn_cmd_attrs; IPC_WaitConnection* conn_cmd = NULL; char path[] = "path"; char socketpath[] = HA_LOGDAEMON_IPC; GMainLoop* mainloop; mainloop = g_main_new(FALSE); G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM, logd_term_action,mainloop, NULL); conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(conn_cmd_attrs, path, socketpath); conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs); g_hash_table_destroy(conn_cmd_attrs); if (conn_cmd == NULL){ fprintf(stderr, "ERROR: create waiting connection failed"); exit(1); } /*Create a source to handle new connect rquests for command*/ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE , on_connect_cmd, chan, NULL); chan->ops->set_high_flow_callback(chan, logd_suspend_clients, NULL); chan->ops->set_low_flow_callback(chan, logd_resume_clients, NULL); chan->high_flow_mark = chan->send_queue->max_qlen; chan->low_flow_mark = (chan->send_queue->max_qlen*3)/4; G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan, FALSE,NULL,NULL,NULL); G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP, logd_hup_action, mainloop, NULL); g_main_run(mainloop); return; } static gboolean direct_log(IPC_Channel* ch, gpointer user_data) { IPC_Message* ipcmsg; GMainLoop* loop; int pri = LOG_DEBUG + 1; loop =(GMainLoop*)user_data; while(ch->ops->is_message_pending(ch)){ if (ch->ch_status == IPC_DISCONNECT){ cl_log(LOG_ERR, "read channel is disconnected:" "something very wrong happened"); return FALSE; } ipcmsg = getIPCmsg(ch); if (ipcmsg == NULL){ return TRUE; } if( ipcmsg->msg_body && ipcmsg->msg_len > 0 ){ LogDaemonMsgHdr *logmsghdr; LogDaemonMsgHdr copy; char *msgtext; logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body; /* this copy nonsense is here because apparently ia64 * complained about "unaligned memory access. */ #define COPYFIELD(copy, msg, field) memcpy(((u_char*)©.field), ((u_char*)&msg->field), sizeof(copy.field)) COPYFIELD(copy, logmsghdr, use_pri_str); COPYFIELD(copy, logmsghdr, entity); COPYFIELD(copy, logmsghdr, entity_pid); COPYFIELD(copy, logmsghdr, timestamp); COPYFIELD(copy, logmsghdr, priority); /* Don't want to copy the following message text */ msgtext = (char *)logmsghdr + sizeof(LogDaemonMsgHdr); cl_direct_log(copy.priority, msgtext , copy.use_pri_str , copy.entity, copy.entity_pid , copy.timestamp); if (copy.priority < pri) pri = copy.priority; (void)logd_log; /* if (verbose){ logd_log("%s[%d]: %s %s\n", logmsg->entity[0]=='\0'? "unknown": copy.entity, copy.entity_pid, ha_timestamp(copy.timestamp), msgtext); } */ if (ipcmsg->msg_done){ ipcmsg->msg_done(ipcmsg); } } } /* current message backlog processed, * about to return to mainloop, * fflush and potentially fsync stuff */ cl_log_do_fflush(pri <= LOG_ERR); if(needs_shutdown) { cl_log(LOG_INFO, "Exiting write process"); g_main_quit(loop); return FALSE; } return TRUE; } static gboolean logd_term_write_action(int sig, gpointer userdata) { /* as a side-effect, the log message makes sure we enter direct_log() * one last time (so we always exit) */ needs_shutdown = TRUE; cl_log(LOG_INFO, "logd_term_write_action: received SIGTERM"); cl_log(LOG_DEBUG, "Writing out %d messages then quitting", (int)chanspair[WRITE_PROC_CHAN]->recv_queue->current_qlen); direct_log(chanspair[WRITE_PROC_CHAN], userdata); return TRUE; } static void write_msg_process(IPC_Channel* readchan) { GMainLoop* mainloop; IPC_Channel* ch = readchan; mainloop = g_main_new(FALSE); G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, ch, FALSE, direct_log, mainloop, NULL); G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM, logd_term_write_action, mainloop, NULL); G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP, logd_hup_action, mainloop, NULL); g_main_run(mainloop); } static void usage(void) { printf("usage: \n" "%s [options]\n\n" "options: \n" "-d make the program a daemon\n" "-k stop the logging daemon if it is already running\n" "-s return logging daemon status \n" "-c use this config file\n" "-v verbosely print debug messages" "-h print out this message\n\n", cmdname); return; } int main(int argc, char** argv, char** envp) { int c; gboolean daemonize = FALSE; gboolean stop_logd = FALSE; gboolean ask_status= FALSE; const char* cfgfile = NULL; pid_t pid; cmdname = argv[0]; while ((c = getopt(argc, argv, "c:dksvh")) != -1){ switch(c){ case 'd': /* daemonize */ daemonize = TRUE; break; case 'k': /* stop */ stop_logd = TRUE; break; case 's': /* status */ ask_status = TRUE; break; case 'c': /* config file*/ cfgfile = optarg; break; case 'v': verbose = TRUE; break; case 'h': /*help message */ default: usage(); exit(1); } } set_ipc_time_debug_flag(FALSE); cl_log_set_uselogd(FALSE); if (!cfgfile && access(DEFAULT_CFG_FILE, F_OK) == 0) { cfgfile = DEFAULT_CFG_FILE; } /* default one set to "logd" * by setting facility, we enable syslog */ cl_log_enable_stderr(TRUE); cl_log_set_entity(logd_config.entity); cl_log_set_facility(logd_config.log_facility); if (ask_status){ long pid; if( (pid = cl_read_pidfile(LOGD_PIDFILE)) > 0 ){ printf("logging daemon is running [pid = %ld].\n", pid); exit(LSB_EXIT_OK); }else{ if (pid == - LSB_STATUS_VAR_PID) { printf("logging daemon is stopped: %s exists.\n" , LOGD_PIDFILE); }else{ printf("logging daemon is stopped.\n"); } } exit(-pid); } if (stop_logd){ logd_stop(); exit(LSB_EXIT_OK); } logd_make_daemon(daemonize); if (ipc_channel_pair(chanspair) != IPC_OK){ cl_perror("cannot create channel pair IPC"); return -1; } if (cfgfile && !parse_config(cfgfile)) { cl_log(LOG_ERR, "Config file [%s] is incorrect." , cfgfile); exit(LSB_EXIT_NOTCONFIGED); } if (strlen(logd_config.debugfile) > 0) { cl_log_set_debugfile(logd_config.debugfile); } if (strlen(logd_config.logfile) > 0) { cl_log_set_logfile(logd_config.logfile); } cl_log_set_syslogprefix(logd_config.syslogprefix); cl_log_set_entity(logd_config.entity); cl_log_set_facility(logd_config.log_facility); cl_log(LOG_INFO, "logd started with %s.", cfgfile ? cfgfile : "default configuration"); if (cl_enable_coredumps(TRUE) < 0){ cl_log(LOG_ERR, "enabling core dump failed"); } cl_cdtocoredir(); chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN], LOGD_QUEUE_LEN); chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN], LOGD_QUEUE_LEN); if (init_set_proc_title(argc, argv, envp) < 0) { cl_log(LOG_ERR, "Allocation of proc title failed."); return -1; } switch(pid = fork()){ case -1: cl_perror("Can't fork child process!"); return -1; case 0: /*child*/ cl_log_use_buffered_io(1); set_proc_title("ha_logd: write process"); write_msg_process(chanspair[WRITE_PROC_CHAN]); break; default: /*parent*/ set_proc_title("ha_logd: read process"); write_process_pid = pid; /* we don't expect to log anything in the parent. */ cl_log_close_log_files(); read_msg_process(chanspair[READ_PROC_CHAN]); break; } return 0; } Reusable-Cluster-Components-glue--3cff550e1084/logd/ha_logger.10000644000000000000000000000157612120057602024230 0ustar00usergroup00000000000000.TH HA_LOGGER 1 "5th Nov 2004" .SH NAME .B ha_logger \- Log a message to files/syslog through the HA Logging Daemon .SH SYNOPSIS .B ha_logger .RI "[-D ha-log/ha-debug] [-t tag] [message ...]" .P .SH DESCRIPTION .B ha_logger is used to log a message to files/syslog through the HA Logging Daemon .PP .IP "\fB-D\fP \fIha-log/ha-debug\fP" Log the message to different files. Ha-log will log the message to the log file and the debug file, while ha-debug will log the message to the debug file only. .IP "\fB-t\fP \fItag\fP" Mark every line in the log with the specified .IP \fBmessage\fP The message you want to log on. .PP .SH SEE ALSO ha_logd(8) heartbeat(8) .SH DOCUMENTATION More information may be found at .UR http://linux-ha.org/wiki http://linux-ha.org/wiki .UE .SH AUTHORS .nf Guochun Shi Alan Robertson Lars Marowsky-Bree .fi Reusable-Cluster-Components-glue--3cff550e1084/logd/ha_logger.c0000644000000000000000000000611612120057602024305 0ustar00usergroup00000000000000/* * ha_logger.c utility to log a message to the logging daemon * * Copyright (C) 2004 Guochun Shi * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXIT_OK 0 #define EXIT_FAIL 1 int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str); void cl_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3); static void usage(void) { printf("usage: " "ha_logger [-t tag] [-D ] [message]\n"); return; } #define BUFSIZE 1024 int main(int argc, char** argv) { int priority; char* entity = NULL; int c; char buf[BUFSIZE]; const char* logtype = "ha-log"; while (( c =getopt(argc, argv,"t:D:h")) != -1){ switch(c){ case 't': entity = optarg; break; case 'D': logtype=optarg; break; case 'h': usage(); exit(1); default: usage(); exit(1); } } if(!cl_log_test_logd()){ fprintf(stderr, "logd is not running"); return EXIT_FAIL; } argc -=optind; argv += optind; if (entity != NULL){ cl_log_set_entity(entity); } if (strcmp(logtype, "ha-log") == 0){ priority = LOG_INFO; } else if (strcmp(logtype, "ha-debug") == 0){ priority = LOG_DEBUG; }else{ goto err_exit; } if (argc > 0){ register char *p; for (p = *argv; *argv; argv++, p = *argv) { while (strlen(p) > BUFSIZE-1) { memcpy(buf, p, BUFSIZE-1); *(buf+BUFSIZE-1) = '\0'; if (LogToDaemon(priority,buf, BUFSIZE,FALSE) != HA_OK){ return EXIT_FAIL; } p += BUFSIZE-1; } if (LogToDaemon(priority,p, strnlen(p, BUFSIZE),FALSE) != HA_OK){ return EXIT_FAIL; } } return EXIT_OK; }else { while (fgets(buf, sizeof(buf), stdin) != NULL) { /* glibc is buggy and adds an additional newline, so we have to remove it here until glibc is fixed */ int len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; if (LogToDaemon(priority, buf,strlen(buf), FALSE) == HA_OK){ continue; }else { return EXIT_FAIL; } } return EXIT_OK; } err_exit: usage(); return(1); } Reusable-Cluster-Components-glue--3cff550e1084/logd/logd.cf0000644000000000000000000000270312120057602023447 0ustar00usergroup00000000000000# File to write debug messages to # Default: /var/log/ha-debug #debugfile /var/log/ha-debug # # # File to write other messages to # Default: /var/log/ha-log #logfile /var/log/ha-log # # # Octal file permission to create the log files with # Default: 0644 #logmode 0640 # # # Facility to use for syslog()/logger # (set to 'none' to disable syslog logging) # Default: daemon #logfacility daemon # Entity to be shown at beginning of a message # generated by the logging daemon itself # Default: "logd" #entity logd # Entity to be shown at beginning of _every_ message # passed to syslog (not to log files). # # Intended for easier filtering, or safe blacklisting. # You can filter on logfacility and this prefix. # # Message format changes like this: # -Nov 18 11:30:31 soda logtest: [21366]: info: total message dropped: 0 # +Nov 18 11:30:31 soda common-prefix: logtest[21366]: info: total message dropped: 0 # # Default: none (disabled) #syslogprefix linux-ha # Do we register to apphbd # Default: no #useapphbd no # There are two processes running for logging daemon # 1. parent process which reads messages from all client channels # and writes them to the child process # # 2. the child process which reads messages from the parent process through IPC # and writes them to syslog/disk # set the send queue length from the parent process to the child process # #sendqlen 256 # set the recv queue length in child process # #recvqlen 256 Reusable-Cluster-Components-glue--3cff550e1084/logd/logd.in0000755000000000000000000000423412120057602023471 0ustar00usergroup00000000000000#!/bin/sh # # # logd Start logd (non-blocking log service) # # Author: Dejan Muhamedagic # (After the heartbeat init script) # License: GNU General Public License (GPL) # # This script works correctly under SuSE, Debian, # Conectiva, Red Hat and a few others. Please let me know if it # doesn't work under your distribution, and we'll fix it. # We don't hate anyone, and like for everyone to use # our software, no matter what OS or distribution you're using. # # chkconfig: 2345 19 21 # description: Startup script logd service. # processname: ha_logd # pidfile: @localstatedir@/run/logd.pid # config: @sysconfdir@/logd.cf # ### BEGIN INIT INFO # Description: ha_logd is a non-blocking logging daemon. # It can log messages either to a file or through syslog # daemon. # Short-Description: ha_logd logging daemon # Provides: ha_logd # Required-Start: $network $syslog $remote_fs # Required-Stop: $network $syslog $remote_fs # X-Start-Before: heartbeat openais corosync # Default-Start: 3 5 # Default-Stop: 0 1 6 ### END INIT INFO LOGD_CFG=@sysconfdir@/logd.cf LOGD_OPT="" [ -f "$LOGD_CFG" ] && LOGD_OPT="-c $LOGD_CFG" LOGD_BIN="@libdir@/@HB_PKG@/ha_logd" if [ ! -f $LOGD_BIN ]; then echo -n "ha_logd not installed." exit 5 fi StartLogd() { echo -n "Starting ha_logd: " $LOGD_BIN -s >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "logd is already running" return 0 fi $LOGD_BIN -d $LOGD_OPT >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "starting logd failed" exit 1 fi echo "ok" exit 0 } StopLogd() { echo -n "Stopping ha_logd: " $LOGD_BIN -s >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "logd is already stopped" return 0 fi $LOGD_BIN -k >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "stopping logd failed" exit 1 fi echo "stopped" exit 0 } StatusLogd() { $LOGD_BIN -s exit $? } case "$1" in start) StartLogd ;; status) StatusLogd ;; stop) StopLogd ;; restart) sleeptime=1 $0 stop && sleep $sleeptime && $0 start echo ;; try-restart) $0 status && $0 restart ;; *) echo "Usage: $0 {start|stop|status|restart}" exit 1 esac Reusable-Cluster-Components-glue--3cff550e1084/logd/logtest.c0000644000000000000000000000542512120057602024041 0ustar00usergroup00000000000000/* * ha_logger.c utility to log a message to the logging daemon * * Copyright (C) 2004 Guochun Shi * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXIT_OK 0 #define EXIT_FAIL 1 #define MAXMSGSIZE (1048*4) int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str); extern IPC_Channel * get_log_chan(void); static GMainLoop* loop; static gboolean send_log_msg(gpointer data) { static int count = 0; char msgstring[MAXMSGSIZE]; int priority; static int dropmsg = 0; long maxcount = (long) data; IPC_Channel* chan = get_log_chan(); if (chan == NULL){ cl_log(LOG_ERR, "logging channel is NULL"); g_main_quit(loop); return FALSE; } if (count >= maxcount){ cl_log(LOG_INFO, "total message dropped: %d", dropmsg); g_main_quit(loop); return FALSE; } if (chan->send_queue->current_qlen == chan->send_queue->max_qlen){ return TRUE; } priority = LOG_INFO; msgstring[0]=0; snprintf(msgstring, sizeof(msgstring),"Message %d", count++); fprintf(stderr, "sending %s\n", msgstring); if (LogToDaemon(priority, msgstring,MAXMSGSIZE, FALSE) != HA_OK){ printf("sending out messge %d failed\n", count); dropmsg++; } return TRUE; } static void usage(char* prog) { printf("Usage:%s \n", prog); return; } int main(int argc, char** argv) { long maxcount; if (argc < 2){ usage(argv[0]); return 1; } maxcount = atoi(argv[1]); cl_log_set_entity("logtest"); cl_log_set_facility(HA_LOG_FACILITY); cl_log_set_uselogd(TRUE); if(!cl_log_test_logd()){ return EXIT_FAIL; } cl_log_set_logd_channel_source(NULL, NULL); g_idle_add(send_log_msg, (gpointer)maxcount); loop = g_main_loop_new(NULL, FALSE); g_main_run(loop); return(1); } Reusable-Cluster-Components-glue--3cff550e1084/lrm/Makefile.am0000644000000000000000000000154312120057602024112 0ustar00usergroup00000000000000# Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = lrmd admin test Reusable-Cluster-Components-glue--3cff550e1084/lrm/admin/Makefile.am0000644000000000000000000000300012120057602025170 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl halibdir = $(libdir)/@HB_PKG@ COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la $(GLIBLIB) LRM_DIR = lrm sbin_PROGRAMS = lrmadmin sbin_SCRIPTS = cibsecret lrmadmin_SOURCES = lrmadmin.c lrmadmin_LDFLAGS = $(COMMONLIBS) lrmadmin_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la lrmadmin_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la if BUILD_HELP man8_MANS = $(sbin_PROGRAMS:%=%.8) %.8: % echo Creating $@ chmod a+x $< help2man --output $@ --no-info --section 8 --name "Part of the Linux-HA project" $(top_builddir)/lrm/admin/$< endif Reusable-Cluster-Components-glue--3cff550e1084/lrm/admin/cibsecret.in0000755000000000000000000002016012120057602025440 0ustar00usergroup00000000000000#!/bin/sh # Copyright (C) 2011 Dejan Muhamedagic # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # WARNING: # # The CIB secrets interface and implementation is still being # discussed, it may change # # cibsecret: manage the secrets directory /var/lib/heartbeat/lrm/secrets # # secrets are ascii files, holding just one value per file: # /var/lib/heartbeat/lrm/secrets// # # NB: this program depends on utillib.sh # . @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs HA_NOARCHBIN=@datadir@/@PACKAGE_NAME@ . $HA_NOARCHBIN/utillib.sh LRM_CIBSECRETS=$HA_VARLIB/lrm/secrets PROG=`basename $0` SSH_OPTS="-o StrictHostKeyChecking=no" usage() { cat< -C: don't read/write the CIB command: set | delete | stash | unstash | get | check | sync set get check stash (if not -C) unstash (if not -C) delete sync stash/unstash: move the parameter from/to the CIB (if you already have the parameter set in the CIB). set/delete: add/remove a parameter from the local file. get: display the parameter from the local file. check: verify MD5 hash of the parameter from the local file and the CIB. sync: copy $LRM_CIBSECRETS to other nodes. Examples: $PROG set ipmi_node1 passwd SecreT_PASS $PROG stash ipmi_node1 passwd $PROG get ipmi_node1 passwd $PROG check ipmi_node1 passwd $PROG sync EOF exit $1 } fatal() { echo "ERROR: $*" exit 1 } warn() { echo "WARNING: $*" } info() { echo "INFO: $*" } check_env() { which md5sum >/dev/null 2>&1 || fatal "please install md5sum to run $PROG" if which pssh >/dev/null 2>&1; then rsh=pssh_fun rcp=pscp_fun elif which pdsh >/dev/null 2>&1; then rsh=pdsh_fun rcp=pdcp_fun elif which ssh >/dev/null 2>&1; then rsh=ssh_fun rcp=scp_fun else fatal "please install pssh, pdsh, or ssh to run $PROG" fi ps -ef | grep '[c]rmd' >/dev/null || fatal "pacemaker not running? $PROG needs pacemaker" } get_other_nodes() { crm_node -l | awk '{print $2}' | grep -v `uname -n` } check_down_nodes() { local n down_nodes down_nodes=`(for n; do echo $n; done) | sort | uniq -u` if [ -n "$down_nodes" ]; then if [ `echo $down_nodes | wc -w` = 1 ]; then warn "node $down_nodes is down" warn "you'll need to update it using $PROG sync later" else warn "nodes `echo $down_nodes` are down" warn "you'll need to update them using $PROG sync later" fi fi } pssh_fun() { pssh -qi -H "$nodes" -x "$SSH_OPTS" $* } pscp_fun() { pscp -q -H "$nodes" -x "-pr" -x "$SSH_OPTS" $* } pdsh_fun() { local pdsh_nodes=`echo $nodes | tr ' ' ','` export PDSH_SSH_ARGS_APPEND="$SSH_OPTS" pdsh -w $pdsh_nodes $* } pdcp_fun() { local pdsh_nodes=`echo $nodes | tr ' ' ','` export PDSH_SSH_ARGS_APPEND="$SSH_OPTS" pdcp -pr -w $pdsh_nodes $* } ssh_fun() { local h for h in $nodes; do ssh $SSH_OPTS $h $* || return done } scp_fun() { local h src="$1" dest=$2 for h in $nodes; do scp -pr -q $SSH_OPTS $src $h:$dest || return done } # TODO: this procedure should be replaced with csync2 # provided that csync2 has already been configured sync_files() { local crm_nodes=`get_other_nodes` local nodes=`get_live_nodes $crm_nodes` check_down_nodes $nodes $crm_nodes [ "$nodes" = "" ] && { info "no other nodes live" return } info "syncing $LRM_CIBSECRETS to `echo $nodes` ..." $rsh rm -rf $LRM_CIBSECRETS && $rsh mkdir -p `dirname $LRM_CIBSECRETS` && $rcp $LRM_CIBSECRETS `dirname $LRM_CIBSECRETS` } sync_one() { local f=$1 f_all="$1 $1.sign" local crm_nodes=`get_other_nodes` local nodes=`get_live_nodes $crm_nodes` check_down_nodes $nodes $crm_nodes [ "$nodes" = "" ] && { info "no other nodes live" return } info "syncing $f to `echo $nodes` ..." $rsh mkdir -p `dirname $f` && if [ -f "$f" ]; then $rcp "$f_all" `dirname $f` else $rsh rm -f $f_all fi } is_secret() { # assume that the secret is in the CIB if we cannot talk to # cib [ "$NO_CRM" ] || test "$1" = "$MAGIC" } check_cib_rsc() { local rsc=$1 output output=`$NO_CRM crm_resource -r $rsc -W >/dev/null 2>&1` || fatal "resource $rsc doesn't exist: $output" } get_cib_param() { local rsc=$1 param=$2 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -g $param 2>/dev/null } set_cib_param() { local rsc=$1 param=$2 value=$3 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -p $param -v "$value" 2>/dev/null } remove_cib_param() { local rsc=$1 param=$2 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -d $param 2>/dev/null } localfiles() { local cmd=$1 local rsc=$2 param=$3 value=$4 local local_file=$LRM_CIBSECRETS/$rsc/$param case $cmd in "get") cat $local_file 2>/dev/null true ;; "getsum") cat $local_file.sign 2>/dev/null true ;; "set") local md5sum md5sum=`printf $value | md5sum` || fatal "md5sum failed to produce hash for resource $rsc parameter $param" md5sum=`echo $md5sum | awk '{print $1}'` mkdir -p `dirname $local_file` && echo $value > $local_file && echo $md5sum > $local_file.sign && sync_one $local_file ;; "remove") rm -f $local_file sync_one $local_file ;; *) # not reached, this is local interface ;; esac } get_local_param() { local rsc=$1 param=$2 localfiles get $rsc $param } set_local_param() { local rsc=$1 param=$2 value=$3 localfiles set $rsc $param $value } remove_local_param() { local rsc=$1 param=$2 localfiles remove $rsc $param } cibsecret_set() { local value=$1 if [ -z "$NO_CRM" ]; then [ "$current" -a "$current" != "$MAGIC" -a "$current" != "$value" ] && fatal "CIB value <$current> different for $rsc parameter $param; please delete it first" fi set_local_param $rsc $param $value && set_cib_param $rsc $param "$MAGIC" } cibsecret_check() { local md5sum local_md5sum is_secret "$current" || fatal "resource $rsc parameter $param not set as secret, nothing to check" local_md5sum=`localfiles getsum $rsc $param` [ "$local_md5sum" ] || fatal "no MD5 hash for resource $rsc parameter $param" md5sum=`printf "$current_local" | md5sum | awk '{print $1}'` [ "$md5sum" = "$local_md5sum" ] || fatal "MD5 hash mismatch for resource $rsc parameter $param" } cibsecret_get() { cibsecret_check echo "$current_local" } cibsecret_delete() { remove_local_param $rsc $param && remove_cib_param $rsc $param } cibsecret_stash() { [ "$NO_CRM" ] && fatal "no access to Pacemaker, stash not supported" [ "$current" = "" ] && fatal "nothing to stash for resource $rsc parameter $param" is_secret "$current" && fatal "resource $rsc parameter $param already set as secret, nothing to stash" cibsecret_set "$current" } cibsecret_unstash() { [ "$NO_CRM" ] && fatal "no access to Pacemaker, unstash not supported" [ "$current_local" = "" ] && fatal "nothing to unstash for resource $rsc parameter $param" is_secret "$current" || warn "resource $rsc parameter $param not set as secret, but we have local value so proceeding anyway" remove_local_param $rsc $param && set_cib_param $rsc $param $current_local } cibsecret_sync() { sync_files } check_env MAGIC="lrm://" umask 0077 if [ "$1" = "-C" ]; then NO_CRM=':' shift 1 fi cmd=$1 rsc=$2 param=$3 value=$4 case "$cmd" in set) [ $# -ne 4 ] && usage 1;; get) [ $# -ne 3 ] && usage 1;; check) [ $# -ne 3 ] && usage 1;; stash) [ $# -ne 3 ] && usage 1;; unstash) [ $# -ne 3 ] && usage 1;; delete) [ $# -ne 3 ] && usage 1;; sync) [ $# -ne 1 ] && usage 1;; *) usage 1; esac # we'll need these two often current=`get_cib_param $rsc $param` current_local=`get_local_param $rsc $param` cibsecret_$cmd $value Reusable-Cluster-Components-glue--3cff550e1084/lrm/admin/lrmadmin.c0000644000000000000000000006732712120057602025131 0ustar00usergroup00000000000000/* File: lrmadmin.c * Description: A adminstration tool for Local Resource Manager * * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * Todo: security verification * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #ifndef __USE_GNU #define __USE_GNU /* For strnlen protype */ #include #undef __USE_GNU #else #include #endif #include #ifdef HAVE_GETOPT_H #include #endif /* HAVE_GETOPT_H */ #include #include #include #include #include #include #include static const char *optstring = "A:D:X:dE:F:dg:p:M:O:P:c:S:LI:CT:n:hv"; #ifdef HAVE_GETOPT_H static struct option long_options[] = { {"daemon", 0, NULL, 'd'}, {"executera", 1, NULL, 'E'}, {"flush", 1, NULL, 'F'}, {"state", 1, NULL, 'S'}, {"listall", 0, NULL, 'L'}, {"information", 1, NULL, 'I'}, {"add", 1, NULL, 'A'}, {"delete", 1, NULL, 'D'}, {"fail", 1, NULL, 'X'}, {"raclass_supported", 1, NULL, 'C'}, {"ratype_supported", 1, NULL, 'T'}, {"all_type_metadata", 1, NULL, 'O'}, {"metadata", 1, NULL, 'M'}, {"provider", 1, NULL, 'P'}, {"set_lrmd_param", 1, NULL, 'p'}, {"get_lrmd_param", 1, NULL, 'g'}, {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'v'}, {NULL, 0, NULL, 0} }; #endif /* HAVE_GETOPT_H */ static GMainLoop *mainloop; static const char *lrmadmin_name = "lrmadmin"; static const char *fake_name; /* 20 is the length limit for a argv[x] */ static const int ARGVI_MAX_LEN = 48; typedef enum { ERROR_OPTION = -1, NULL_OP, DAEMON_OP, EXECUTE_RA, FLUSH, RSC_STATE, LIST_ALLRSC, INF_RSC, SET_PARAM, GET_PARAM, ADD_RSC, DEL_RSC, FAIL_RSC, RACLASS_SUPPORTED, RATYPE_SUPPORTED, RA_METADATA, RA_PROVIDER, ALL_RA_METADATA, HELP } lrmadmin_cmd_t; #define nullcheck(p) ((p) ? (p) : "") static const char * status_msg[6] = { "pending", /* LRM_OP_PENDING */ "succeed", /* LRM_OP_DONE */ "cancelled", /* LRM_OP_CANCELLED */ "timeout", /* LRM_OP_TIMEOUT */ "not Supported", /* LRM_OP_NOTSUPPORTED */ "failed due to an error" /* LRM_OP_ERROR */ }; static const char * rc_msg[] = { "unknown error", "no ra", "ok", "unknown error", "invalid parameter", "unimplement feature", "insufficient priority", "not installed", "not configured", "not running", "running master", "failed master", "invalid rc", /* For status command only */ "daemon dead1", "daemon dead2", "daemon stopped", "status unknow" }; static gboolean QUIT_GETOPT = FALSE; static lrmadmin_cmd_t lrmadmin_cmd = NULL_OP; static gboolean ASYN_OPS = FALSE; static int call_id = 0; static int TIMEOUT = -1; /* the unit is ms */ static const char *simple_help_screen = "lrmadmin -d,--daemon\n" " -A,--add []\n" " -D,--delete \n" " -F,--flush \n" " -X,--fail [ []]\n" " -E,--execute []\n" " -S,--state [-n ]\n" " -L,--listall\n" " -I,--information \n" " -C,--raclass_supported\n" " -T,--ratype_supported \n" " -O,--all metadata of this class \n" " -M,--metadata \n" " -P,--provider \n" " -p,--set_lrmd_param \n" " -g,--get_lrmd_param \n" " -v,--version\n" " -h,--help\n"; #define OPTION_OBSCURE_CHECK \ if ( lrmadmin_cmd != NULL_OP ) { \ cl_log(LOG_ERR,"Obscure options."); \ return -1; \ } /* the begin of the internal used function list */ static int resource_operation(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[]); static int add_resource(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[]); static int fail_resource(ll_lrm_t * lrmd, char *rsc_id, int optc, char *opts[]); static int get_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]); static int set_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]); static int transfer_cmd_params(int amount, int start, char * argv[], const char * class, GHashTable ** params_ht); static void g_print_stringitem_and_free(gpointer data, gpointer user_data); static void g_print_rainfo_item_and_free(gpointer data, gpointer user_data); static void g_print_ops(gpointer data, gpointer user_data); static void g_get_rsc_description(gpointer data, gpointer user_data); static void g_print_meta(gpointer key, gpointer value, gpointer user_data); static void print_rsc_inf(lrm_rsc_t * lrmrsc); static char * params_hashtable_to_str(const char * class, GHashTable * ht); static void free_stritem_of_hashtable(gpointer key, gpointer value, gpointer user_data); static void ocf_params_hash_to_str(gpointer key, gpointer value, gpointer user_data); static void normal_params_hash_to_str(gpointer key, gpointer value, gpointer user_data); static lrm_rsc_t * get_lrm_rsc(ll_lrm_t * lrmd, char * rscid); static int ra_metadata(ll_lrm_t * lrmd, int argc, int optind, char * argv[]); static int ra_provider(ll_lrm_t * lrmd, int argc, int optind, char * argv[]); static gboolean lrmd_output_dispatch(IPC_Channel* notused, gpointer user_data); static gboolean lrm_op_timeout(gpointer data); /* the end of the internal used function list */ static void lrm_op_done_callback(lrm_op_t* op); static int ret_value; int main(int argc, char **argv) { int option_char; char rscid_arg_tmp[RID_LEN]; ll_lrm_t* lrmd; lrm_rsc_t * lrm_rsc; GList *raclass_list = NULL, *ratype_list = NULL, *rscid_list; GHashTable *all_meta = NULL; char raclass[20]; const char * login_name = lrmadmin_name; /* Prevent getopt_long to print error message on stderr itself */ /*opterr = 0; */ if (argc == 1) { printf("%s",simple_help_screen); return 0; } cl_log_set_entity(lrmadmin_name); cl_log_enable_stderr(TRUE); cl_log_set_facility(LOG_USER); memset(rscid_arg_tmp, '\0', RID_LEN); memset(raclass, '\0', 20); do { #ifdef HAVE_GETOPT_H option_char = getopt_long (argc, argv, optstring, long_options, NULL); #else option_char = getopt (argc, argv, optstring); #endif if (option_char == -1) { break; } switch (option_char) { case 'd': OPTION_OBSCURE_CHECK lrmadmin_cmd = DAEMON_OP; QUIT_GETOPT = TRUE; break; case 'A': OPTION_OBSCURE_CHECK lrmadmin_cmd = ADD_RSC; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'D': OPTION_OBSCURE_CHECK lrmadmin_cmd = DEL_RSC; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'X': OPTION_OBSCURE_CHECK lrmadmin_cmd = FAIL_RSC; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'C': OPTION_OBSCURE_CHECK lrmadmin_cmd = RACLASS_SUPPORTED; break; case 'T': OPTION_OBSCURE_CHECK lrmadmin_cmd = RATYPE_SUPPORTED; if (optarg) { strncpy(raclass, optarg, 19); } break; case 'O': OPTION_OBSCURE_CHECK lrmadmin_cmd = ALL_RA_METADATA; if (optarg) { strncpy(raclass, optarg, 19); } break; case 'F': OPTION_OBSCURE_CHECK lrmadmin_cmd = FLUSH; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'E': OPTION_OBSCURE_CHECK lrmadmin_cmd = EXECUTE_RA; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'M': OPTION_OBSCURE_CHECK lrmadmin_cmd = RA_METADATA; break; case 'P': OPTION_OBSCURE_CHECK lrmadmin_cmd = RA_PROVIDER; break; case 'S': OPTION_OBSCURE_CHECK lrmadmin_cmd = RSC_STATE; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'L': OPTION_OBSCURE_CHECK lrmadmin_cmd = LIST_ALLRSC; break; case 'I': OPTION_OBSCURE_CHECK lrmadmin_cmd = INF_RSC; strncpy(rscid_arg_tmp, optarg, RID_LEN-1); break; case 'p': OPTION_OBSCURE_CHECK lrmadmin_cmd = SET_PARAM; break; case 'g': OPTION_OBSCURE_CHECK lrmadmin_cmd = GET_PARAM; break; case 'n': if (optarg) { fake_name = optarg; } break; case 'v': printf("%s\n",GLUE_VERSION); return 0; case 'h': OPTION_OBSCURE_CHECK printf("%s",simple_help_screen); return 0; case '?': /* cl_log(LOG_ERR,"There is a unrecognized option %s", optarg); */ printf("%s", simple_help_screen); return -1; default: cl_log(LOG_ERR,"getopt returned character" " code %c.", option_char); return -1; } } while (!QUIT_GETOPT); lrmd = ll_lrm_new("lrm"); if (NULL == lrmd) { cl_log(LOG_ERR,"ll_lrm_new returned NULL."); return -2; } lrmd->lrm_ops->set_lrm_callback(lrmd, lrm_op_done_callback); if (fake_name != NULL) { login_name = fake_name; } if (lrmd->lrm_ops->signon(lrmd, login_name) != 1) { /* != HA_OK */ printf("lrmd is not running.\n"); if (lrmadmin_cmd == DAEMON_OP) { return LSB_STATUS_STOPPED; } else { cl_log(LOG_WARNING,"Can't connect to lrmd!"); return -2; } } if (lrmadmin_cmd == DAEMON_OP) { printf("lrmd is stopped.\n"); lrmd->lrm_ops->signoff(lrmd); return 0; } switch (lrmadmin_cmd) { case EXECUTE_RA: call_id = resource_operation(lrmd, rscid_arg_tmp, argc, optind, argv); if (call_id < 0) { if ( call_id == -2 ) { cl_log(LOG_ERR, "Failed to operate " "resource %s due to parameter error." , argv[optind]); ret_value = -3; } if ( call_id == -1 ) { cl_log(LOG_WARNING, "Failed! No such " "resource %s.", argv[optind]); ret_value = -2; } else { cl_log(LOG_ERR, "Failed to operate " "resource %s due to unknown error." , argv[optind]); ret_value = -3; } ASYN_OPS = FALSE; } else { /* Return value: HA_OK = 1 Or HA_FAIL = 0 */ if ( call_id == 0 ) { cl_log(LOG_ERR, "Resource operation " "failed." ); ret_value = -3; ASYN_OPS = FALSE; } else { ASYN_OPS = TRUE; } } break; case RA_METADATA: ra_metadata(lrmd, argc, optind, argv); ASYN_OPS = FALSE; break; case RA_PROVIDER: ra_provider(lrmd, argc, optind, argv); ASYN_OPS = FALSE; break; case SET_PARAM: set_param(lrmd, argc, optind, argv); ASYN_OPS = FALSE; break; case GET_PARAM: get_param(lrmd, argc, optind, argv); ASYN_OPS = FALSE; break; case ADD_RSC: if (add_resource(lrmd, rscid_arg_tmp, argc, optind, argv) == 0) { printf("Succeeded in adding this resource.\n"); } else { printf("Failed to add this resource.\n"); ret_value = -3; } ASYN_OPS = FALSE; break; case DEL_RSC: /* Return value: HA_OK = 1 Or HA_FAIL = 0 */ if (lrmd->lrm_ops->delete_rsc(lrmd, rscid_arg_tmp)==1) { printf("Succeeded in deleting this resource.\n"); } else { printf("Failed to delete this resource.\n"); ret_value = -3; } ASYN_OPS = FALSE; break; case FAIL_RSC: /* Return value: HA_OK = 1 Or HA_FAIL = 0 */ if (fail_resource(lrmd, rscid_arg_tmp, argc-optind, argv+optind) == 1) { printf("Succeeded in failing the resource.\n"); } else { printf("Failed to fail the resource.\n"); ret_value = -3; } ASYN_OPS = FALSE; break; case FLUSH: lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp); if (!(lrm_rsc)) { ret_value = -3; } else { /* Return value: HA_OK = 1 Or HA_FAIL = 0 */ if (lrm_rsc->ops->flush_ops(lrm_rsc) == 1 ) { printf("Succeeded in flushing.\n"); } else { printf("Failed to flush.\n"); ret_value = -3; } lrm_free_rsc(lrm_rsc); } ASYN_OPS = FALSE; break; case RACLASS_SUPPORTED: raclass_list = lrmd->lrm_ops-> get_rsc_class_supported(lrmd); printf("There are %d RA classes supported:\n", g_list_length(raclass_list)); if (raclass_list) { g_list_foreach(raclass_list, g_print_stringitem_and_free, NULL); g_list_free(raclass_list); ret_value = LSB_EXIT_OK; } else { printf("No RA classes found!\n"); ret_value = -3; } ASYN_OPS = FALSE; break; case RATYPE_SUPPORTED: ratype_list = lrmd->lrm_ops-> get_rsc_type_supported(lrmd, raclass); printf("There are %d RAs:\n", g_list_length(ratype_list)); if (ratype_list) { g_list_foreach(ratype_list, g_print_rainfo_item_and_free, NULL); g_list_free(ratype_list); } ASYN_OPS = FALSE; break; case ALL_RA_METADATA: all_meta = lrmd->lrm_ops->get_all_type_metadata(lrmd, raclass); if (all_meta) { g_hash_table_foreach(all_meta, g_print_meta, NULL); g_hash_table_destroy(all_meta); } ASYN_OPS = FALSE; break; case LIST_ALLRSC: rscid_list = lrmd->lrm_ops->get_all_rscs(lrmd); if (rscid_list) { g_list_foreach(rscid_list, g_get_rsc_description , lrmd); g_list_free(rscid_list); } else printf("Currently no resources are managed by " "LRM.\n"); ASYN_OPS = FALSE; break; case INF_RSC: lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp); if (!(lrm_rsc)) { ret_value = -3; } else { print_rsc_inf(lrm_rsc); lrm_free_rsc(lrm_rsc); } ASYN_OPS = FALSE; break; case RSC_STATE: lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp); if (!(lrm_rsc)) { ret_value = -3; } else { state_flag_t cur_state = LRM_RSC_IDLE; GList * ops_queue; ops_queue = lrm_rsc->ops->get_cur_state(lrm_rsc, &cur_state); printf("resource state:%s\n", cur_state==LRM_RSC_IDLE? "LRM_RSC_IDLE":"LRM_RSC_BUSY"); printf("The resource %d operations' " "information:\n" , g_list_length(ops_queue)); if (ops_queue) { g_list_foreach(ops_queue, g_print_ops, NULL); lrm_free_op_list(ops_queue); } lrm_free_rsc(lrm_rsc); } ASYN_OPS = FALSE; break; default: fprintf(stderr, "Option %c is not supported yet.\n", option_char); ret_value = -1; ASYN_OPS = FALSE; break; } if (ASYN_OPS) { G_main_add_IPC_Channel(G_PRIORITY_LOW, lrmd->lrm_ops->ipcchan(lrmd), FALSE, lrmd_output_dispatch, lrmd, NULL); if (TIMEOUT > 0) { Gmain_timeout_add(TIMEOUT, lrm_op_timeout, &ret_value); } mainloop = g_main_new(FALSE); printf( "Waiting for lrmd to callback...\n"); g_main_run(mainloop); } lrmd->lrm_ops->signoff(lrmd); return ret_value; } static gboolean lrm_op_timeout(gpointer data) { int * idata = data; printf("ERROR: This operation has timed out - no result from lrmd.\n"); *idata = -5; g_main_quit(mainloop); return FALSE; } static gboolean lrmd_output_dispatch(IPC_Channel* notused, gpointer user_data) { ll_lrm_t *lrm = (ll_lrm_t*)user_data; lrm->lrm_ops->rcvmsg(lrm, FALSE); g_main_quit(mainloop); return TRUE; } static void lrm_op_done_callback(lrm_op_t* op) { if (!op) { cl_log(LOG_ERR, "In callback function, op is NULL pointer."); ret_value = -3; return; } printf("----------------operation--------------\n"); printf("type:%s\n", op->op_type); if ( (0 == STRNCMP_CONST(op->op_type, "status") || 0 == STRNCMP_CONST(op->op_type, "monitor")) && (op->rc == 7) ) { printf("operation status:%s\n", status_msg[LRM_OP_DONE-LRM_OP_PENDING]); printf("op_status: %d\n", LRM_OP_DONE); } else { printf("operation status:%s\n", status_msg[(op->op_status - LRM_OP_PENDING) % DIMOF(status_msg)]); printf("op_status: %d\n", op->op_status); } printf("return code: %d\n", op->rc); printf("output data: \n%s\n", (op->output ? op->output : "[null]")); printf("---------------------------------------\n\n"); ret_value = op->rc; } static int resource_operation(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[]) { GHashTable * params_ht = NULL; lrm_op_t op = lrm_zero_op; lrm_rsc_t * lrm_rsc; int call_id; if ((argc - optind) < 3) { cl_log(LOG_ERR,"Not enough parameters."); return -2; } lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rsc_id); if (!lrm_rsc) { return -1; } op.op_type = argv[optind]; op.timeout = atoi(argv[optind+1]); /* When op.timeout!=0, plus additional 1s. Or lrmadmin may time out before the normal operation result returned from lrmd. This may be redudant, but harmless. */ if (0 < op.timeout ) { TIMEOUT = op.timeout + 1000; } op.interval = atoi(argv[optind+2]); op.user_data = NULL; op.user_data_len = 0; if (0 == strcmp(argv[optind+3], "EVERYTIME")) { op.target_rc = EVERYTIME; } else if (0 == strcmp(argv[optind+3], "CHANGED")) { op.target_rc = CHANGED; } else { op.target_rc = atoi(argv[optind+3]); } if ((argc - optind) > 3) { if (0 > transfer_cmd_params(argc, optind+4, argv, lrm_rsc->class, ¶ms_ht) ) { return -2; } } op.params = params_ht; call_id = lrm_rsc->ops->perform_op(lrm_rsc, &op); lrm_free_rsc(lrm_rsc); if (params_ht) { g_hash_table_foreach(params_ht, free_stritem_of_hashtable, NULL); g_hash_table_destroy(params_ht); } return call_id; } static int ra_metadata(ll_lrm_t * lrmd, int argc, int optind, char * argv[]) { const char * class = argv[optind-1]; const char * type = argv[optind]; const char * provider = argv[optind+1]; char* metadata; if(argc < 5) { cl_log(LOG_ERR,"Not enough parameters."); return -2; } if (0 == strncmp(provider,"NULL",strlen("NULL"))) { provider=NULL; } metadata = lrmd->lrm_ops->get_rsc_type_metadata(lrmd, class, type, provider); if (NULL!=metadata) { printf ("%s\n", metadata); g_free (metadata); } return 0; } static int ra_provider(ll_lrm_t * lrmd, int argc, int optind, char * argv[]) { const char * class = argv[optind-1]; const char * type = argv[optind]; GList* providers = NULL; GList* provider = NULL; if(argc < 4) { cl_log(LOG_ERR,"Not enough parameters."); return -2; } providers = lrmd->lrm_ops->get_rsc_provider_supported(lrmd,class,type); while (NULL != (provider = g_list_first(providers))) { printf("%s\n",(char*)provider->data); g_free(provider->data); providers = g_list_remove(providers, provider->data); } g_list_free(providers); return 0; } static int add_resource(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[]) { const char * class = argv[optind]; const char * type = argv[optind+1]; const char * provider = argv[optind+2]; GHashTable * params_ht = NULL; int tmp_ret; if ((argc - optind) < 3) { cl_log(LOG_ERR,"Not enough parameters."); return -2; } if (0 == strncmp(provider, "NULL", strlen("NULL"))) { provider=NULL; } /* delete Hashtable */ if ((argc - optind) > 3) { if ( 0 > transfer_cmd_params(argc, optind+3, argv, class, ¶ms_ht) ) { return -1; } } tmp_ret = lrmd->lrm_ops->add_rsc(lrmd, rsc_id, class, type, provider, params_ht); /*delete params_ht*/ if (params_ht) { g_hash_table_foreach(params_ht, free_stritem_of_hashtable, NULL); g_hash_table_destroy(params_ht); } return (tmp_ret ? 0 : -1); /* tmp_ret is HA_OK=1 or HA_FAIL=0 */ } static int fail_resource(ll_lrm_t * lrmd, char *rsc_id, int optc, char *opts[]) { int fail_rc = 0; const char * reason = NULL; if (optc > 2) { cl_log(LOG_ERR,"Bad usage."); return -2; } if (optc >= 1) fail_rc = atoi(opts[0]); if (optc == 2) reason = opts[1]; return lrmd->lrm_ops->fail_rsc(lrmd, rsc_id, fail_rc, reason); } static int get_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]) { const char *name = argv[optind-1]; char *value; if ((argc - optind) != 0) { cl_log(LOG_ERR,"Bad usage."); return -2; } value = lrmd->lrm_ops->get_lrmd_param(lrmd, name); printf("%s: %s\n", name, value); return 0; } static int set_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]) { const char *name = argv[optind-1]; const char *value = argv[optind]; if ((argc - optind) != 1) { cl_log(LOG_ERR,"Bad usage."); return -2; } return lrmd->lrm_ops->set_lrmd_param(lrmd, name, value); } static int transfer_cmd_params(int amount, int start, char * argv[], const char * class, GHashTable ** params_ht) { int i, len_tmp; char * delimit, * key, * value; char buffer[21]; if (amount < start) { return -1; } if ( strncmp("ocf", class, strlen("ocf"))==0 || strncmp("stonith", class, strlen("stonith"))==0) { *params_ht = g_hash_table_new(g_str_hash, g_str_equal); for (i=start; ilen+1); strncpy(params_str, gstr_tmp->str, gstr_tmp->len+1); g_string_free(gstr_tmp, TRUE); } else if ( strncmp("lsb", class, strlen("lsb")) == 0 || strncmp("heartbeat", class, strlen("heartbeat")) == 0 ) { ht_size = g_hash_table_size(ht); if (ht_size == 0) { return NULL; } tmp_str = g_new(gchar, ht_size*ARGVI_MAX_LEN); memset(tmp_str, ' ', ht_size*ARGVI_MAX_LEN); tmp_str[ht_size*ARGVI_MAX_LEN-1] = '\0'; g_hash_table_foreach(ht, normal_params_hash_to_str, &tmp_str); gstr_tmp = g_string_new(""); for (i=0; i< ht_size; i++) { gstr_tmp = g_string_append(gstr_tmp , tmp_str + i*ARGVI_MAX_LEN ); gstr_tmp = g_string_append(gstr_tmp, " "); } params_str = g_new(gchar, gstr_tmp->len+1); strncpy(params_str, gstr_tmp->str, gstr_tmp->len+1); g_string_free(gstr_tmp, TRUE); } else { fprintf(stderr, "Not supported resource agent class.\n"); } return params_str; } static void g_print_stringitem_and_free(gpointer data, gpointer user_data) { printf("%s\n", (char*)data); g_free(data); } static void g_print_rainfo_item_and_free(gpointer data, gpointer user_data) { printf("%s\n", (char *)data); g_free(data); } static void g_print_ops(gpointer data, gpointer user_data) { lrm_op_t* op = (lrm_op_t*)data; GString * param_gstr; time_t run_at=0, rcchange_at=0; if (NULL == op) { cl_log(LOG_ERR, "%s:%d: op==NULL" , __FUNCTION__, __LINE__); return; } param_gstr = g_string_new(""); g_hash_table_foreach(op->params, ocf_params_hash_to_str, ¶m_gstr); if( op->t_run ) run_at=(time_t)op->t_run; if( op->t_rcchange ) rcchange_at=(time_t)op->t_rcchange; printf(" operation '%s' [call_id=%d]:\n" " start_delay=%d, interval=%d, timeout=%d, app_name=%s\n" " rc=%d (%s), op_status=%d (%s)\n" , nullcheck(op->op_type), op->call_id , op->start_delay, op->interval, op->timeout , nullcheck(op->app_name), op->rc , rc_msg[(op->rc-EXECRA_EXEC_UNKNOWN_ERROR) % DIMOF(rc_msg)] , op->op_status , status_msg[(op->op_status-LRM_OP_PENDING) % DIMOF(status_msg)] ); if( op->t_run || op->t_rcchange ) printf(" run at: %s" " last rc change at: %s" " queue time: %lums, exec time: %lums\n" , op->t_run ? ctime(&run_at) : "N/A\n" , op->t_rcchange ? ctime(&rcchange_at) : "N/A\n" , op->queue_time, op->exec_time ); printf(" parameters: %s\n", param_gstr->str); g_string_free(param_gstr, TRUE); } static void g_get_rsc_description(gpointer data, gpointer user_data) { ll_lrm_t* lrmd = (ll_lrm_t *)user_data; lrm_rsc_t * lrm_rsc; char rsc_id_tmp[RID_LEN]; if (!(user_data)) { return; } memset(rsc_id_tmp, '\0', RID_LEN); strncpy(rsc_id_tmp, data, RID_LEN-1); lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rsc_id_tmp); if (lrm_rsc) { print_rsc_inf(lrm_rsc); lrm_free_rsc(lrm_rsc); } else cl_log(LOG_ERR, "Invalid resource id: %s.", rsc_id_tmp); g_free(data); } static void g_print_meta(gpointer key, gpointer value, gpointer user_data) { printf("%s\n", (const char*)key); printf("%s\n", (const char*)value); } static void print_rsc_inf(lrm_rsc_t * lrm_rsc) { char rscid_str_tmp[RID_LEN]; char * tmp = NULL; if (!lrm_rsc) { return; } rscid_str_tmp[RID_LEN-1] = '\0'; strncpy(rscid_str_tmp, lrm_rsc->id, RID_LEN-1); printf("\nResource ID:%s\n", rscid_str_tmp); printf("Resource agent class:%s\n", lrm_rsc->class); printf("Resource agent type:%s\n", lrm_rsc->type); printf("Resource agent provider:%s\n" , lrm_rsc->provider?lrm_rsc->provider:"default"); if (lrm_rsc->params) { tmp = params_hashtable_to_str(lrm_rsc->class, lrm_rsc->params); printf("Resource agent parameters:%s\n" , (tmp == NULL) ? "No parameter" : tmp); if (tmp != NULL) { g_free(tmp); } } } static void free_stritem_of_hashtable(gpointer key, gpointer value, gpointer user_data) { /*printf("key=%s value=%s\n", (char *)key, (char *)value);*/ g_free(key); g_free(value); } static void ocf_params_hash_to_str(gpointer key, gpointer value, gpointer user_data) { GString * gstr_tmp = *(GString **)user_data; gstr_tmp = g_string_append(gstr_tmp, (char*)key); gstr_tmp = g_string_append(gstr_tmp, "="); gstr_tmp = g_string_append(gstr_tmp, (char *)value); gstr_tmp = g_string_append(gstr_tmp, " "); } static void normal_params_hash_to_str(gpointer key, gpointer value, gpointer user_data) { gint key_int; gchar * str_tmp = *(gchar **) user_data; if (str_tmp == NULL ) { return; } key_int = atoi((char *)key) - 1; if( key_int < 0 ) { return; } strncpy(str_tmp + key_int * ARGVI_MAX_LEN, (char*)value, ARGVI_MAX_LEN - 1); } static lrm_rsc_t * get_lrm_rsc(ll_lrm_t * lrmd, char * rscid) { char uuid_str_tmp[RID_LEN]; lrm_rsc_t * lrm_rsc; lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rscid); if (!(lrm_rsc)) { uuid_str_tmp[RID_LEN-1] = '\0'; strncpy(uuid_str_tmp, rscid, RID_LEN-1); cl_log(LOG_ERR,"Resource %s does not exist.", uuid_str_tmp); } return lrm_rsc; } Reusable-Cluster-Components-glue--3cff550e1084/lrm/admin/lrmadmin.txt0000644000000000000000000000306712120057602025515 0ustar00usergroup00000000000000# LRM Admin Command-line Interface # It's a draft NAME lrmadmin - Local Resource Manager Commander-line Daministrator Tools SYNOPSIS lrmadmin {-d|--daemon} {-A|--add} [] {-D|--delete} {-F|--flush} {-E|--execute} [] {-M|--monitor} -s [] {-M|--monitor} {-g|-c} {-S|--status} {-L|--listall} {-I|--information} {-R|--rasupported} {-h|--help} Detailed Explanation for Options Lrmd daemon options {-d|--daemon} # -s The status of lrmd: running or not running # -r Reset lrmd (?) Resource options {-A|--add} [] Add a resource. {-D|--delete} Delete a resource {-E|--execute} [] Let resource agent to performance the operation {-F|--flush} Clear all pending operation on the resource agnency {-M|--monitor} {-s|-g} -s rscname Set a monitors on the resource agent -g Get the information about the monitors on the resource agent {-S|--status} Get the status of current resource agent {-L|--listall} List all available resource agent {-I|--information} List the information about a resource {-R|--rasupported} List the support types of resource agent such as OCF and etc. Other options {-h|--help} Display the help screen Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/Makefile.am0000644000000000000000000000303412120057602025045 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2002 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) halibdir = $(libdir)/@HB_PKG@ COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \ $(GLIBLIB) # $(top_builddir)/lib/apphb/libapphb.la halib_PROGRAMS = lrmd lrmd_SOURCES = lrmd.c audit.c cib_secrets.c lrmd_fdecl.h lrmd.h lrmd_LDFLAGS = $(top_builddir)/lib/lrm/liblrm.la \ $(COMMONLIBS) @LIBLTDL@ \ $(top_builddir)/lib/pils/libpils.la noinst_HEADERS = lrmd_fdecl.h lrmd.h # make lrmd's owner as hacluster:haclient? Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/audit.c0000644000000000000000000001202712120057602024265 0ustar00usergroup00000000000000/* * Audit lrmd global data structures * * Author: Dejan Muhamedagic * Copyright (c) 2007 Novell GmbH * * 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 software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_APPHB # include #endif #include #include #include #include #ifdef DOLRMAUDITS extern GHashTable* clients; extern GHashTable* resources; #define ptr_bad(level,p,item,text) \ lrmd_log(level,"LRMAUDIT: 0x%lx unallocated pointer for: %s(%s)", \ (unsigned long)p,item,text); #define ptr_null(level,item,text) \ lrmd_log(level,"LRMAUDIT: pointer null for: %s(%s)", \ item,text); /* NB: this macro contains return */ #define ret_on_null(p,item,text) do { \ if( !p ) { \ ptr_bad(LOG_INFO,p,item,text); \ return; \ } \ } while(0) #define log_on_null(p,item,text) do { \ if( !p ) { \ ptr_null(LOG_INFO,item,text); \ } \ } while(0) void lrmd_audit(const char *function, int line) { lrmd_log(LOG_DEBUG, "LRMAUDIT: in %s:%d",function,line); #ifdef LRMAUDIT_CLIENTS audit_clients(); #endif #ifdef LRMAUDIT_RESOURCES audit_resources(); #endif } void audit_clients() { g_hash_table_foreach(clients, on_client, NULL); } void audit_resources() { g_hash_table_foreach(resources, on_resource, NULL); } void audit_ops(GList* rsc_ops, lrmd_rsc_t* rsc, const char *desc) { GList *oplist; for( oplist = g_list_first(rsc_ops); oplist; oplist = g_list_next(oplist) ) { on_op(oplist->data, rsc, desc); } } void on_client(gpointer key, gpointer value, gpointer user_data) { lrmd_client_t * client = (lrmd_client_t*)value; ret_on_null(client,"","client"); log_on_null(client->app_name,"","app_name"); log_on_null(client->ch_cmd,client->app_name,"ch_cmd"); log_on_null(client->ch_cbk,client->app_name,"ch_cbk"); log_on_null(client->g_src,client->app_name,"g_src"); log_on_null(client->g_src_cbk,client->app_name,"g_src_cbk"); } void on_resource(gpointer key, gpointer value, gpointer user_data) { lrmd_rsc_t* rsc = (lrmd_rsc_t*)value; ret_on_null(rsc,"","rsc"); ret_on_null(rsc->id,"","id"); log_on_null(rsc->type,rsc->id,"type"); log_on_null(rsc->class,rsc->id,"class"); log_on_null(rsc->provider,rsc->id,"provider"); /*log_on_null(rsc->params,rsc->id,"params");*/ log_on_null(rsc->last_op_table,rsc->id,"last_op_table"); log_on_null(rsc->last_op_done,rsc->id,"last_op_done"); audit_ops(rsc->op_list,rsc,"op_list"); audit_ops(rsc->repeat_op_list,rsc,"repeat_op_list"); } void on_op(lrmd_op_t *op, lrmd_rsc_t* rsc, const char *desc) { ret_on_null(op,rsc->id,desc); log_on_null(op->rsc_id,rsc->id,"rsc_id"); if( strcmp(op->rsc_id,rsc->id) ) { lrmd_log(LOG_ERR,"LRMAUDIT: rsc %s, op %s " "op->rsc_id does not match rsc->id", rsc->id,small_op_info(op)); } log_on_null(op->msg,small_op_info(op),"msg"); if( op->rapop ) { if( op->rapop->lrmd_op != op ) { lrmd_log(LOG_ERR, "LRMAUDIT: rsc %s, op %s: rapop->lrmd_op does not match op", rsc->id,small_op_info(op)); } if( strcmp(op->rapop->rsc_id,op->rsc_id) ) { lrmd_log(LOG_ERR, "LRMAUDIT: rsc %s, op %s rapop->rsc_id does not match op->rsc_id", rsc->id,small_op_info(op)); } on_ra_pipe_op(op->rapop,op,"rapop"); } } void on_ra_pipe_op(ra_pipe_op_t *rapop, lrmd_op_t *op, const char *desc) { ret_on_null(rapop,small_op_info(op),desc); log_on_null(rapop->ra_stdout_gsource,small_op_info(op),"ra_stdout_gsource"); log_on_null(rapop->ra_stderr_gsource,small_op_info(op),"ra_stderr_gsource"); log_on_null(rapop->rsc_id,small_op_info(op),"rsc_id"); log_on_null(rapop->op_type,small_op_info(op),"op_type"); log_on_null(rapop->rsc_class,small_op_info(op),"rsc_class"); if( strcmp(op->rsc_id,rapop->rsc_id) ) { lrmd_log(LOG_ERR,"LRMAUDIT: %s: rapop->rsc_id " "does not match op_rsc->id", small_op_info(op)); } } #endif /*DOLRMAUDITS*/ Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/cib_secrets.c0000644000000000000000000001260712120057602025450 0ustar00usergroup00000000000000/* * cib_secrets.c * * Author: Dejan Muhamedagic * Copyright (c) 2011 SUSE, Attachmate * * 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 software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int replace_secret_params(char *rsc_id, GHashTable* params); static int is_magic_value(char *p); static int check_md5_hash(char *hash, char *value); static void add_secret_params(gpointer key, gpointer value, gpointer user_data); static char *read_local_file(char *local_file); #define MAGIC "lrm://" static int is_magic_value(char *p) { return !strcmp(p, MAGIC); } #define MD5LEN 16 static int check_md5_hash(char *hash, char *value) { int i; char hash2[2*MD5LEN+1]; unsigned char binary[MD5LEN+1]; MD5((unsigned char *)value, strlen(value), binary); for (i = 0; i < MD5LEN; i++) sprintf(hash2+2*i, "%02x", binary[i]); hash2[2*i] = '\0'; lrmd_debug2(LOG_DEBUG , "%s:%d: hash: %s, calculated hash: %s" , __FUNCTION__, __LINE__, hash, hash2); return !strcmp(hash, hash2); } static char * read_local_file(char *local_file) { FILE *fp = fopen(local_file, "r"); char buf[MAX_VALUE_LEN+1]; char *p; if (!fp) { if (errno != ENOENT) { cl_perror("%s:%d: cannot open %s" , __FUNCTION__, __LINE__, local_file); } return NULL; } if (!fgets(buf, MAX_VALUE_LEN, fp)) { cl_perror("%s:%d: cannot read %s" , __FUNCTION__, __LINE__, local_file); return NULL; } /* strip white space */ for (p = buf+strlen(buf)-1; p >= buf && isspace(*p); p--) ; *(p+1) = '\0'; return g_strdup(buf); } /* * returns 0 on success or no replacements necessary * returns -1 if replacement failed for whatever reasone */ int replace_secret_params(char *rsc_id, GHashTable* params) { char local_file[FILENAME_MAX+1], *start_pname; char hash_file[FILENAME_MAX+1], *hash; GList *secret_params = NULL, *l; char *key, *pvalue, *secret_value; int rc = 0; /* secret_params could be cached with the resource; * there are also parameters sent with operations * which cannot be cached */ g_hash_table_foreach(params, add_secret_params, &secret_params); if (!secret_params) /* none found? */ return 0; lrmd_debug(LOG_DEBUG , "%s:%d: replace secret parameters for resource %s" , __FUNCTION__, __LINE__, rsc_id); if (snprintf(local_file, FILENAME_MAX, LRM_CIBSECRETS "/%s/", rsc_id) > FILENAME_MAX) { lrmd_log(LOG_ERR , "%s:%d: filename size exceeded for resource %s" , __FUNCTION__, __LINE__, rsc_id); return -1; } start_pname = local_file + strlen(local_file); for (l = g_list_first(secret_params); l; l = g_list_next(l)) { key = (char *)(l->data); pvalue = g_hash_table_lookup(params, key); if (!pvalue) { /* this cannot really happen */ lrmd_log(LOG_ERR , "%s:%d: odd, no parameter %s for rsc %s found now" , __FUNCTION__, __LINE__, key, rsc_id); continue; } if ((strlen(key) + strlen(local_file)) >= FILENAME_MAX-2) { lrmd_log(LOG_ERR , "%s:%d: parameter name %s too big" , __FUNCTION__, __LINE__, key); rc = -1; continue; } strcpy(start_pname, key); secret_value = read_local_file(local_file); if (!secret_value) { lrmd_log(LOG_ERR , "%s:%d: secret for rsc %s parameter %s " "not found in " LRM_CIBSECRETS , __FUNCTION__, __LINE__, rsc_id, key); rc = -1; continue; } strcpy(hash_file, local_file); if (strlen(hash_file) + 5 > FILENAME_MAX) { lrmd_log(LOG_ERR , "%s:%d: cannot build such a long name " "for the sign file: %s.sign" , __FUNCTION__, __LINE__, hash_file); } else { strncat(hash_file, ".sign", 5); hash = read_local_file(hash_file); if (!check_md5_hash(hash, secret_value)) { lrmd_log(LOG_ERR , "%s:%d: md5 sum for rsc %s parameter %s " "does not match" , __FUNCTION__, __LINE__, rsc_id, key); g_free(secret_value); g_free(hash); rc = -1; continue; } g_free(hash); } g_hash_table_replace(params, g_strdup(key), secret_value); } g_list_free(secret_params); return rc; } static void add_secret_params(gpointer key, gpointer value, gpointer user_data) { GList **lp = (GList **)user_data; if (is_magic_value((char *)value)) *lp = g_list_append(*lp, (char *)key); } Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/lrmd.c0000644000000000000000000030770212120057602024124 0ustar00usergroup00000000000000/* * Local Resource Manager Daemon * * Author: Huang Zhen * Partly contributed by Andrew Beekhof * Copyright (c) 2004 International Business Machines * * 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 software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_APPHB # include #endif /* #include */ #include #include #include #include #include static gboolean in_alloc_dump = FALSE; ProcTrack_ops ManagedChildTrackOps = { on_ra_proc_finished, on_ra_proc_registered, on_ra_proc_query_name }; /* msg dispatch table */ typedef int (*msg_handler)(lrmd_client_t* client, struct ha_msg* msg); struct msg_map { const char *msg_type; int reply_time; msg_handler handler; int min_priv; /* minimum privileges required */ }; /* * two ways to handle replies: * REPLY_NOW: pack whatever the handler returned and send it * NO_MSG: the handler will send the reply itself */ #define REPLY_NOW 0 #define NO_MSG 1 #define send_msg_now(p) \ (p->reply_time==REPLY_NOW) struct msg_map msg_maps[] = { {REGISTER, REPLY_NOW, on_msg_register, 0}, {GETRSCCLASSES, NO_MSG, on_msg_get_rsc_classes, 0}, {GETRSCTYPES, NO_MSG, on_msg_get_rsc_types, 0}, {GETPROVIDERS, NO_MSG, on_msg_get_rsc_providers, 0}, {ADDRSC, REPLY_NOW, on_msg_add_rsc, PRIV_ADMIN}, {GETRSC, NO_MSG, on_msg_get_rsc, PRIV_ADMIN}, {GETLASTOP, NO_MSG, on_msg_get_last_op, PRIV_ADMIN}, {GETALLRCSES, NO_MSG, on_msg_get_all, PRIV_ADMIN}, {DELRSC, REPLY_NOW, on_msg_del_rsc, PRIV_ADMIN}, {FAILRSC, REPLY_NOW, on_msg_fail_rsc, PRIV_ADMIN}, {PERFORMOP, REPLY_NOW, on_msg_perform_op, PRIV_ADMIN}, {FLUSHOPS, REPLY_NOW, on_msg_flush_all, PRIV_ADMIN}, {CANCELOP, REPLY_NOW, on_msg_cancel_op, PRIV_ADMIN}, {GETRSCSTATE, NO_MSG, on_msg_get_state, PRIV_ADMIN}, {GETRSCMETA, NO_MSG, on_msg_get_metadata, 0}, {SETLRMDPARAM, REPLY_NOW, on_msg_set_lrmd_param, PRIV_ADMIN}, {GETLRMDPARAM, NO_MSG, on_msg_get_lrmd_param, 0}, }; #define MSG_NR sizeof(msg_maps)/sizeof(struct msg_map) GHashTable* clients = NULL; /* a GHashTable indexed by pid */ GHashTable* resources = NULL; /* a GHashTable indexed by rsc_id */ static GMainLoop* mainloop = NULL; static int call_id = 1; static const char* lrm_system_name = "lrmd"; static GHashTable * RAExecFuncs = NULL; static GList* ra_class_list = NULL; static gboolean shutdown_in_progress = FALSE; static unsigned long apphb_interval = 2000; /* Millisecond */ static gboolean reg_to_apphbd = FALSE; static int max_child_count = 4; static int retry_interval = 1000; /* Millisecond */ static int child_count = 0; static IPC_Auth * auth = NULL; static struct { int opcount; int clientcount; int rsccount; }lrm_objectstats; /* define indexes into logmsg_ctrl_defs */ #define OP_STAYED_TOO_LONG 0 static struct logspam logmsg_ctrl_defs[] = { { "operation stayed too long in the queue", 10, 60, 120, /* max 10 messages in 60s, then delay for 120s */ "configuration advice: reduce operation contention " "either by increasing lrmd max_children or by increasing intervals " "of monitor operations" }, }; #define set_fd_opts(fd,opts) do { \ int flag; \ if ((flag = fcntl(fd, F_GETFL)) >= 0) { \ if (fcntl(fd, F_SETFL, flag|opts) < 0) { \ cl_perror("%s::%d: fcntl", __FUNCTION__ \ , __LINE__); \ } \ } else { \ cl_perror("%s::%d: fcntl", __FUNCTION__, __LINE__); \ } \ } while(0) static ra_pipe_op_t * ra_pipe_op_new(int child_stdout, int child_stderr, lrmd_op_t * lrmd_op) { ra_pipe_op_t * rapop; lrmd_rsc_t* rsc = NULL; if ( NULL == lrmd_op ) { lrmd_log(LOG_WARNING , "%s:%d: lrmd_op==NULL, no need to malloc ra_pipe_op" , __FUNCTION__, __LINE__); return NULL; } rapop = calloc(sizeof(ra_pipe_op_t), 1); if ( rapop == NULL) { lrmd_log(LOG_ERR, "%s:%d out of memory" , __FUNCTION__, __LINE__); return NULL; } rapop->first_line_read = FALSE; /* * No any obviouse proof of lrmd hang in pipe read yet. * Bug 475 may be a duplicate of bug 499. * Anyway, via test, it's proved that NOBLOCK read will * obviously reduce the RA execution time (bug 553). */ /* Let the read operation be NONBLOCK */ set_fd_opts(child_stdout,O_NONBLOCK); set_fd_opts(child_stderr,O_NONBLOCK); /* there's so much code duplication here */ rapop->ra_stdout_fd = child_stdout; if (rapop->ra_stdout_fd <= STDERR_FILENO) { lrmd_log(LOG_ERR, "%s: invalid stdout fd [%d]" , __FUNCTION__, rapop->ra_stdout_fd); } rapop->ra_stdout_gsource = G_main_add_fd(G_PRIORITY_HIGH , child_stdout, FALSE, handle_pipe_ra_stdout , rapop, destroy_pipe_ra_stdout); rapop->ra_stderr_fd = child_stderr; if (rapop->ra_stderr_fd <= STDERR_FILENO) { lrmd_log(LOG_ERR, "%s: invalid stderr fd [%d]" , __FUNCTION__, rapop->ra_stderr_fd); } rapop->ra_stderr_gsource = G_main_add_fd(G_PRIORITY_HIGH , child_stderr, FALSE, handle_pipe_ra_stderr , rapop, destroy_pipe_ra_stderr); rapop->lrmd_op = lrmd_op; rapop->op_type = strdup(ha_msg_value(lrmd_op->msg, F_LRM_OP)); rapop->rsc_id = strdup(lrmd_op->rsc_id); rsc = lookup_rsc(lrmd_op->rsc_id); if (rsc == NULL) { lrmd_debug(LOG_WARNING , "%s::%d: the rsc (id=%s) does not exist" , __FUNCTION__, __LINE__, lrmd_op->rsc_id); rapop->rsc_class = NULL; } else { rapop->rsc_class = strdup(rsc->class); } return rapop; } static void ra_pipe_op_destroy(ra_pipe_op_t * rapop) { CHECK_ALLOCATED(rapop, "ra_pipe_op", ); if ( NULL != rapop->ra_stdout_gsource) { G_main_del_fd(rapop->ra_stdout_gsource); rapop->ra_stdout_gsource = NULL; } if ( NULL != rapop->ra_stderr_gsource) { G_main_del_fd(rapop->ra_stderr_gsource); rapop->ra_stderr_gsource = NULL; } if (rapop->ra_stdout_fd >= STDERR_FILENO) { close(rapop->ra_stdout_fd); rapop->ra_stdout_fd = -1; }else if (rapop->ra_stdout_fd >= 0) { lrmd_log(LOG_ERR, "%s: invalid stdout fd %d" , __FUNCTION__, rapop->ra_stdout_fd); } if (rapop->ra_stderr_fd >= STDERR_FILENO) { close(rapop->ra_stderr_fd); rapop->ra_stderr_fd = -1; }else if (rapop->ra_stderr_fd >= 0) { lrmd_log(LOG_ERR, "%s: invalid stderr fd %d" , __FUNCTION__, rapop->ra_stderr_fd); } rapop->first_line_read = FALSE; free(rapop->rsc_id); free(rapop->op_type); rapop->op_type = NULL; free(rapop->rsc_class); rapop->rsc_class = NULL; if (rapop->lrmd_op != NULL) { rapop->lrmd_op->rapop = NULL; rapop->lrmd_op = NULL; } free(rapop); } static void lrmd_op_destroy(lrmd_op_t* op) { CHECK_ALLOCATED(op, "op", ); --lrm_objectstats.opcount; if (op->exec_pid > 1) { lrmd_log(LOG_CRIT , "%s: lingering operation process %d, op %s" , __FUNCTION__, op->exec_pid, small_op_info(op)); return; } lrmd_debug2(LOG_DEBUG, "%s: free the %s with address %p" ,__FUNCTION__, op_info(op), op); ha_msg_del(op->msg); op->msg = NULL; if( op->rsc_id ) { free(op->rsc_id); op->rsc_id = NULL; } op->exec_pid = 0; if ( op->rapop != NULL ) { op->rapop->lrmd_op = NULL; op->rapop = NULL; } op->first_line_ra_stdout[0] = EOS; if( op->repeat_timeout_tag ) { Gmain_timeout_remove(op->repeat_timeout_tag); } free(op); } static lrmd_op_t* lrmd_op_new(void) { lrmd_op_t* op = (lrmd_op_t*)calloc(sizeof(lrmd_op_t),1); if (op == NULL) { lrmd_log(LOG_ERR, "lrmd_op_new(): out of memory when " "calloc a lrmd_op_t."); return NULL; } op->rsc_id = NULL; op->msg = NULL; op->exec_pid = -1; op->repeat_timeout_tag = 0; op->rapop = NULL; op->first_line_ra_stdout[0] = EOS; op->t_recv = time_longclock(); op->t_perform = zero_longclock; op->t_done = zero_longclock; op->t_rcchange = zero_longclock; op->t_lastlogmsg = zero_longclock; memset(op->killseq, 0, sizeof(op->killseq)); ++lrm_objectstats.opcount; return op; } static lrmd_op_t* lrmd_op_copy(const lrmd_op_t* op) { lrmd_op_t* ret; ret = lrmd_op_new(); if (NULL == ret) { return NULL; } /* Do a "shallow" copy */ *ret = *op; /* * Some things, like timer ids and child pids are duplicated here * but can be destroyed in one copy, but kept intact * in the other, to later be destroyed. * This isn't a complete disaster, since the timer ids aren't * pointers, but it's still untidy at the least. * Be sure and care of this situation when using this function. */ /* Do a "deep" copy of the message structure */ ret->rapop = NULL; ret->msg = ha_msg_copy(op->msg); ret->rsc_id = strdup(op->rsc_id); ret->rapop = NULL; ret->first_line_ra_stdout[0] = EOS; ret->repeat_timeout_tag = 0; ret->exec_pid = -1; ret->t_recv = op->t_recv; ret->t_perform = op->t_perform; ret->t_done = op->t_done; ret->t_rcchange = op->t_rcchange; ret->is_copy = TRUE; ret->is_cancelled = FALSE; ret->weight = op->weight; return ret; } static const char * op_status_to_str(int op_status) { static char whatwasthat[25]; switch (op_status) { case LRM_OP_DONE: return "LRM_OP_DONE"; case LRM_OP_CANCELLED: return "LRM_OP_CANCELLED"; case LRM_OP_TIMEOUT: return "LRM_OP_TIMEOUT"; case LRM_OP_NOTSUPPORTED: return "LRM_OP_NOTSUPPORTED"; case -1: return "N/A (-1)"; default: break; } snprintf(whatwasthat, sizeof(whatwasthat), "UNDEFINED STATUS: %d?", op_status); return whatwasthat; } static const char * op_target_rc_to_str(int target) { static char whatwasthat[25]; switch (target) { case EVERYTIME: return "EVERYTIME"; case CHANGED: return "CHANGED"; default: break; } snprintf(whatwasthat, sizeof(whatwasthat) ,"UNDEFINED TARGET_RC: %d", target); return whatwasthat; } /* * We need a separate function to dump out operations for * debugging. Then we wouldn't have to have the code for this * inline. In particular, we could then call this from on_op_done() * which would shorten and simplify that code - which could use * the help :-) */ /* Debug oriented funtions */ static gboolean debug_level_adjust(int nsig, gpointer user_data); static void lrmd_op_dump(const lrmd_op_t* op, const char * text) { int op_status = -1; int target_rc = -1; const char * pidstat; longclock_t now = time_longclock(); CHECK_ALLOCATED(op, "op", ); if (op->exec_pid < 1 || ((kill(op->exec_pid, 0) < 0) && ESRCH == errno)) { pidstat = "not running"; }else{ pidstat = "running"; } ha_msg_value_int(op->msg, F_LRM_OPSTATUS, &op_status); ha_msg_value_int(op->msg, F_LRM_TARGETRC, &target_rc); lrmd_debug(LOG_DEBUG , "%s: lrmd_op: %s status: %s, target_rc=%s, client pid %d call_id" ": %d, child pid: %d (%s) %s %s" , text, op_info(op), op_status_to_str(op_status) , op_target_rc_to_str(target_rc) , op->client_id, op->call_id, op->exec_pid, pidstat , (op->is_copy ? "copy" : "original") , (op->is_cancelled ? "cancelled" : "")); lrmd_debug(LOG_DEBUG , "%s: lrmd_op2: rt_tag: %d, interval: %d, delay: %d" , text, op->repeat_timeout_tag , op->interval, op->delay); lrmd_debug(LOG_DEBUG , "%s: lrmd_op3: t_recv: %ldms, t_add: %ldms" ", t_perform: %ldms, t_done: %ldms, t_rcchange: %ldms" , text, tm2age(op->t_recv), tm2age(op->t_addtolist) , tm2age(op->t_perform), tm2age(op->t_done), tm2age(op->t_rcchange)); lrmd_rsc_dump(op->rsc_id, text); } static void lrmd_client_destroy(lrmd_client_t* client) { CHECK_ALLOCATED(client, "client", ); --lrm_objectstats.clientcount; /* * Delete direct references to this client * and repeating operations it might have scheduled */ unregister_client(client); if (client->app_name) { free(client->app_name); client->app_name = NULL; } free(client); } static lrmd_client_t* lrmd_client_new(void) { lrmd_client_t* client; client = calloc(sizeof(lrmd_client_t), 1); if (client == NULL) { lrmd_log(LOG_ERR, "lrmd_client_new(): out of memory when " "calloc lrmd_client_t."); return NULL; } client->g_src = NULL; client->g_src_cbk = NULL; ++lrm_objectstats.clientcount; return client; } static void lrmd_client_dump(gpointer key, gpointer value, gpointer user_data) { lrmd_client_t * client = (lrmd_client_t*)value; CHECK_ALLOCATED(client, "client", ); if(!client) { return; } lrmd_debug(LOG_DEBUG, "client name: %s, client pid: %d" ", client uid: %d, gid: %d, last request: %s" ", last op in: %s, lastop out: %s" ", last op rc: %s" , lrm_str(client->app_name) , client->pid , client->uid, client->gid , client->lastrequest , ctime(&client->lastreqstart) , ctime(&client->lastreqend) , ctime(&client->lastrcsent) ); if (!client->ch_cmd) { lrmd_debug(LOG_DEBUG, "NULL client ch_cmd in %s()", __FUNCTION__); }else{ lrmd_debug(LOG_DEBUG , "Command channel status: %d, read queue addr: %p, write queue addr: %p" , client->ch_cmd->ch_status , client->ch_cmd->recv_queue , client->ch_cmd->send_queue ); if (client->ch_cmd->recv_queue && client->ch_cmd->send_queue) { lrmd_debug(LOG_DEBUG, "read Qlen: %ld, write Qlen: %ld" , (long)client->ch_cmd->recv_queue->current_qlen , (long)client->ch_cmd->send_queue->current_qlen); } } if (!client->ch_cbk) { lrmd_debug(LOG_DEBUG, "NULL client ch_cbk in %s()", __FUNCTION__); }else{ lrmd_debug(LOG_DEBUG , "Callback channel status: %d, read Qlen: %ld, write Qlen: %ld" , client->ch_cbk->ch_status , (long)client->ch_cbk->recv_queue->current_qlen , (long)client->ch_cbk->send_queue->current_qlen); } } static void lrmd_dump_all_clients(void) { static gboolean incall = FALSE; if (incall) { return; } incall = TRUE; lrmd_debug(LOG_DEBUG, "%d clients connected to lrmd" , g_hash_table_size(clients)); g_hash_table_foreach(clients, lrmd_client_dump, NULL); incall = FALSE; } static void lrmd_rsc_destroy(lrmd_rsc_t* rsc) { LRMAUDIT(); CHECK_ALLOCATED(rsc, "resource", ); --lrm_objectstats.rsccount; if( rsc->op_list || rsc->repeat_op_list ) { lrmd_log(LOG_ERR, "%s: refusing to remove resource %s" " which is still holding operations" , __FUNCTION__, lrm_str(rsc->id)); return; } else { lrmd_debug(LOG_DEBUG, "%s: removing resource %s" , __FUNCTION__, lrm_str(rsc->id)); } g_hash_table_remove(resources, rsc->id); if (rsc->id) { free(rsc->id); rsc->id = NULL; } if (rsc->type) { free(rsc->type); rsc->type = NULL; } if (rsc->class) { free(rsc->class); rsc->class = NULL; } if (rsc->provider) { free(rsc->provider); rsc->provider = NULL; } if (NULL != rsc->params) { free_str_table(rsc->params); rsc->params = NULL; } if (rsc->last_op_table) { g_hash_table_foreach_remove(rsc->last_op_table , free_str_hash_pair, NULL); g_hash_table_destroy(rsc->last_op_table); rsc->last_op_table = NULL; } if (rsc->last_op_done) { lrmd_op_destroy(rsc->last_op_done); rsc->last_op_done = NULL; } if (rsc->delay_timeout > 0) { Gmain_timeout_remove(rsc->delay_timeout); rsc->delay_timeout = (guint)0; } free(rsc); LRMAUDIT(); } static lrmd_rsc_t* lrmd_rsc_new(const char * id, struct ha_msg* msg) { lrmd_rsc_t* rsc; rsc = (lrmd_rsc_t *)calloc(sizeof(lrmd_rsc_t),1); if (rsc == NULL) { lrmd_log(LOG_ERR, "%s: out of memory when calloc " "a lrmd_rsc_t", __FUNCTION__); return NULL; } rsc->delay_timeout = (guint)0; if (id) { rsc->id = strdup(id); } if (msg) { rsc->type = strdup(ha_msg_value(msg, F_LRM_RTYPE)); rsc->class = strdup(ha_msg_value(msg, F_LRM_RCLASS)); if (NULL == ha_msg_value(msg, F_LRM_RPROVIDER)) { lrmd_log(LOG_NOTICE, "%s(): No %s field in message" , __FUNCTION__, F_LRM_RPROVIDER); }else{ rsc->provider = strdup(ha_msg_value(msg, F_LRM_RPROVIDER)); if (rsc->provider == NULL) { goto errout; } } if (rsc->id == NULL || rsc->type == NULL || rsc->class == NULL) { goto errout; } } g_hash_table_insert(resources, strdup(id), rsc); ++lrm_objectstats.rsccount; return rsc; errout: lrmd_rsc_destroy(rsc); /* violated property */ /* Or so BEAM thinks :-) */ rsc = NULL; return rsc; } static void dump_op(gpointer key, gpointer val, gpointer data) { lrmd_op_t* lrmd_op = (lrmd_op_t*) val; lrmd_op_dump(lrmd_op, "rsc->last_op_table"); } static void dump_op_table(gpointer key, gpointer val, gpointer data) { GHashTable* table = (GHashTable*) val; g_hash_table_foreach(table, dump_op, data); } static void lrmd_rsc_dump(char* rsc_id, const char * text) { static gboolean incall = FALSE; GList* oplist; lrmd_rsc_t* rsc=NULL; if( rsc_id ) { rsc = lookup_rsc(rsc_id); } else { lrmd_debug(LOG_INFO , "%s:%d: the rsc_id is NULL" , __FUNCTION__, __LINE__); return; } CHECK_ALLOCATED(rsc, "rsc", ); if(!rsc) { return; } /* Avoid infinite recursion loops... */ if (incall) { return; } incall = TRUE; /* TODO: Dump params and last_op_table FIXME */ lrmd_debug(LOG_DEBUG, "%s: BEGIN resource dump", text); lrmd_debug(LOG_DEBUG, "%s: resource %s/%s/%s/%s" , text , lrm_str(rsc->id) , lrm_str(rsc->type) , lrm_str(rsc->class) , lrm_str(rsc->provider)); lrmd_debug(LOG_DEBUG, "%s: rsc->op_list...", text); for(oplist = g_list_first(rsc->op_list); oplist; oplist = g_list_next(oplist)) { lrmd_op_dump(oplist->data, "rsc->op_list"); } lrmd_debug(LOG_DEBUG, "%s: rsc->repeat_op_list...", text); for(oplist = g_list_first(rsc->repeat_op_list); oplist; oplist=g_list_next(oplist)) { lrmd_op_dump(oplist->data, "rsc->repeat_op_list"); } if (rsc->last_op_done != NULL) { lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_done...", text); lrmd_op_dump(rsc->last_op_done, "rsc->last_op_done"); } else { lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_done==NULL", text); } if (rsc->last_op_table) { g_hash_table_foreach(rsc->last_op_table,dump_op_table,NULL); } else { lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_table==NULL", text); } lrmd_debug(LOG_DEBUG, "%s: END resource dump", text); incall = FALSE; }; static void dump_id_rsc_pair(gpointer key, gpointer value, gpointer user_data) { char* rid = (char*)key; char* text = (char*)user_data; lrmd_rsc_dump(rid,text); } static void lrmd_dump_all_resources(void) { static gboolean incall = FALSE; char text[]= "lrmd_dump_all_resources"; if (incall) { return; } incall = TRUE; lrmd_debug(LOG_DEBUG, "%d resources are managed by lrmd" , g_hash_table_size(resources)); g_hash_table_foreach(resources, dump_id_rsc_pair, text); incall = FALSE; } #if 0 static void lrm_debug_running_op(lrmd_op_t* op, const char * text) { char cmd[256]; lrmd_op_dump(op, text); CHECK_ALLOCATED(op, "op", ); if (op->exec_pid >= 1) { /* This really ought to use our logger * So... it might not get forwarded to the central machine * if you're testing with CTS -- FIXME!!! */ snprintf(cmd, sizeof(cmd) , "ps -l -f -s %d | logger -p daemon.info -t 'T/O PS:'" , op->exec_pid); lrmd_debug(LOG_DEBUG, "Running [%s]", cmd); if (system(cmd) != 0) { lrmd_log(LOG_ERR, "Running [%s] failed", cmd); } snprintf(cmd, sizeof(cmd) , "ps axww | logger -p daemon.info -t 't/o ps:'"); lrmd_debug(LOG_DEBUG, "Running [%s]", cmd); if (system(cmd) != 0) { lrmd_log(LOG_ERR, "Running [%s] failed", cmd); } } } #endif int main(int argc, char ** argv) { int req_restart = TRUE; int req_status = FALSE; int req_stop = FALSE; int argerr = 0; int flag; while ((flag = getopt(argc, argv, OPTARGS)) != EOF) { switch(flag) { case 'h': /* Help message */ usage(lrm_system_name, LSB_EXIT_OK); break; case 'v': /* Debug mode, more logs*/ ++debug_level; break; case 's': /* Status */ req_status = TRUE; break; case 'k': /* Stop (kill) */ req_stop = TRUE; break; case 'r': /* Restart */ req_restart = TRUE; break; /* Register to apphbd then monitored by it */ case 'm': reg_to_apphbd = TRUE; break; case 'i': /* Get apphb interval */ if (optarg) { apphb_interval = atoi(optarg); } break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { usage(lrm_system_name, LSB_EXIT_GENERIC); } cl_log_set_entity(lrm_system_name); cl_log_enable_stderr(debug_level?TRUE:FALSE); cl_log_set_facility(HA_LOG_FACILITY); /* Use logd if it's enabled by heartbeat */ cl_inherit_logging_environment(0); if (req_status){ return init_status(PID_FILE, lrm_system_name); } if (req_stop){ return init_stop(PID_FILE); } if (req_restart) { init_stop(PID_FILE); } return init_start(); } int init_status(const char *pid_file, const char *client_name) { long pid = cl_read_pidfile(pid_file); if (pid > 0) { fprintf(stderr, "%s is running [pid: %ld]\n" , client_name, pid); return LSB_STATUS_OK; } fprintf(stderr, "%s is stopped.\n", client_name); return LSB_STATUS_STOPPED; } int init_stop(const char *pid_file) { long pid; int rc = LSB_EXIT_OK; if (pid_file == NULL) { lrmd_log(LOG_ERR, "No pid file specified to kill process"); return LSB_EXIT_GENERIC; } pid = cl_read_pidfile(pid_file); if (pid > 0) { if (CL_KILL((pid_t)pid, SIGTERM) < 0) { rc = (errno == EPERM ? LSB_EXIT_EPERM : LSB_EXIT_GENERIC); fprintf(stderr, "Cannot kill pid %ld\n", pid); }else{ lrmd_log(LOG_INFO, "Signal sent to pid=%ld," " waiting for process to exit", pid); while (CL_PID_EXISTS(pid)) { sleep(1); } } } return rc; } static const char usagemsg[] = "[-srkhv]\n\ts: status\n\tr: restart" "\n\tk: kill\n\tm: register to apphbd\n\ti: the interval of apphb\n\t" "h: help\n\tv: debug\n"; void usage(const char* cmd, int exit_status) { FILE* stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s %s", cmd, usagemsg); fflush(stream); exit(exit_status); } /* * In design, the lrmd should not know the meaning of operation type * and the meaning of rc. This function is just for logging. */ static void warning_on_active_rsc(gpointer key, gpointer value, gpointer user_data) { int op_status, rc; const char* op_type; lrmd_rsc_t* rsc = (lrmd_rsc_t*)value; if (rsc->last_op_done != NULL) { if (HA_OK != ha_msg_value_int(rsc->last_op_done->msg , F_LRM_OPSTATUS, &op_status)) { lrmd_debug(LOG_WARNING ,"resource %s is left in UNKNOWN status." \ "(last op done is damaged..)" ,rsc->id); return; } op_type = ha_msg_value(rsc->last_op_done->msg, F_LRM_OP); if (op_status != LRM_OP_DONE) { lrmd_debug(LOG_WARNING ,"resource %s is left in UNKNOWN status." \ "(last op %s finished without LRM_OP_DONE status.)" ,rsc->id, op_type); return; } if (HA_OK != ha_msg_value_int(rsc->last_op_done->msg , F_LRM_RC, &rc)) { lrmd_debug(LOG_WARNING ,"resource %s is left in UNKNOWN status." \ "(last op done is damaged..)" ,rsc->id); return; } if((rc == 0) && (STRNCMP_CONST(op_type,"start") ==0 ||STRNCMP_CONST(op_type,"monitor") ==0 ||STRNCMP_CONST(op_type,"status") ==0)) { lrmd_debug(LOG_WARNING ,"resource %s is left in RUNNING status." \ "(last op %s finished with rc 0.)" ,rsc->id, op_type); return; } if ((rc !=0 ) && (STRNCMP_CONST(op_type,"start") ==0 ||STRNCMP_CONST(op_type,"stop") ==0)) { lrmd_debug(LOG_WARNING ,"resource %s is left in UNKNOWN status." \ "(last op %s finished with rc %d.)" ,rsc->id, op_type, rc); return; } } } static gboolean lrm_shutdown(void) { lrmd_log(LOG_INFO,"lrmd is shutting down"); if (mainloop != NULL && g_main_is_running(mainloop)) { g_hash_table_foreach(resources, warning_on_active_rsc, NULL); g_main_quit(mainloop); }else { exit(LSB_EXIT_OK); } return FALSE; } static void has_pending_op(gpointer key, gpointer value, gpointer user_data) { lrmd_rsc_t* rsc = (lrmd_rsc_t*)value; int* result = (int*)user_data; if (rsc->op_list != NULL) { *result = TRUE; } } static gboolean can_shutdown() { int has_ops = FALSE; g_hash_table_foreach(resources, has_pending_op, &has_ops); return !has_ops; } gboolean sigterm_action(int nsig, gpointer user_data) { shutdown_in_progress = TRUE; if (can_shutdown()) { lrm_shutdown(); } else { lrmd_log(LOG_INFO, "sigterm_action: shutdown postponed, some operations are still running"); } return TRUE; } static void register_pid(gboolean do_fork, gboolean (*shutdown)(int nsig, gpointer userdata)) { int j; umask(022); for (j=0; j < 3; ++j) { close(j); (void)open("/dev/null", j == 0 ? O_RDONLY : O_WRONLY); } CL_IGNORE_SIG(SIGINT); CL_IGNORE_SIG(SIGHUP); CL_DEFAULT_SIG(SIGPIPE); G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM , shutdown, NULL, NULL); cl_signal_set_interrupt(SIGTERM, 1); cl_signal_set_interrupt(SIGCHLD, 1); /* At least they are harmless, I think. ;-) */ cl_signal_set_interrupt(SIGINT, 0); cl_signal_set_interrupt(SIGHUP, 0); } static int init_using_apphb(void) { #ifdef ENABLE_APPHB char lrmd_instance[40]; if (reg_to_apphbd == FALSE) { return -1; } snprintf(lrmd_instance, sizeof(lrmd_instance), "%s_%ld" , lrm_system_name, (long)getpid()); if (apphb_register(lrm_system_name, lrmd_instance) != 0) { lrmd_log(LOG_ERR, "Failed when trying to register to apphbd."); lrmd_log(LOG_ERR, "Maybe apphbd is not running. Quit."); return -1; } lrmd_log(LOG_INFO, "Registered to apphbd."); apphb_setinterval(apphb_interval); apphb_setwarn(apphb_interval*APPHB_WARNTIME_FACTOR); Gmain_timeout_add(apphb_interval - APPHB_INTVL_DETLA, emit_apphb, NULL); #endif return 0; } static gboolean emit_apphb(gpointer data) { #ifdef ENABLE_APPHB if (reg_to_apphbd == FALSE) { return FALSE; } if (apphb_hb() != 0) { lrmd_log(LOG_ERR, "emit_apphb: Failed to emit an apphb."); reg_to_apphbd = FALSE; return FALSE; }; #endif return TRUE; } static void calc_max_children() { #ifdef _SC_NPROCESSORS_ONLN int nprocs; nprocs = sysconf(_SC_NPROCESSORS_ONLN); if( nprocs < 1 ) { lrmd_log(LOG_WARNING, "%s: couldn't get the number of processors" , __FUNCTION__); } else { if( nprocs/2 > max_child_count ) { max_child_count = nprocs/2; } lrmd_log(LOG_INFO, "max-children set to %d " "(%d processors online)", max_child_count, nprocs); return; } #else lrmd_log(LOG_WARNING, "%s: cannot get the number of processors " "on this platform", __FUNCTION__); #endif lrmd_log(LOG_INFO, "max-children set to %d", max_child_count); } /* main loop of the daemon*/ int init_start () { DIR* dir = NULL; PILPluginUniv * PluginLoadingSystem = NULL; struct dirent* subdir; char* dot = NULL; char* ra_name = NULL; int len; IPC_WaitConnection* conn_cmd = NULL; IPC_WaitConnection* conn_cbk = NULL; GHashTable* conn_cmd_attrs; GHashTable* conn_cbk_attrs; char path[] = IPC_PATH_ATTR; char cmd_path[] = LRM_CMDPATH; char cbk_path[] = LRM_CALLBACKPATH; PILGenericIfMgmtRqst RegisterRqsts[]= { {"RAExec", &RAExecFuncs, NULL, NULL, NULL}, { NULL, NULL, NULL, NULL, NULL} }; if( getenv("LRMD_MAX_CHILDREN") ) { set_lrmd_param("max-children", getenv("LRMD_MAX_CHILDREN")); } else { calc_max_children(); } qsort(msg_maps, MSG_NR, sizeof(struct msg_map), msg_type_cmp); if (cl_lock_pidfile(PID_FILE) < 0) { lrmd_log(LOG_ERR, "already running: [pid %d].", cl_read_pidfile(PID_FILE)); lrmd_log(LOG_ERR, "Startup aborted (already running). Shutting down."); exit(100); } register_pid(FALSE, sigterm_action); /* load RA plugins */ PluginLoadingSystem = NewPILPluginUniv (HA_PLUGIN_DIR); PILLoadPlugin(PluginLoadingSystem, "InterfaceMgr", "generic", &RegisterRqsts); /* * FIXME!!! * Much of the code through the end of the next loop is * unnecessary - The plugin system will do this for you quite * nicely. And, it does it portably, too... */ dir = opendir(LRM_PLUGIN_DIR); if (NULL == dir) { lrmd_log(LOG_ERR, "main: can not open RA plugin dir "LRM_PLUGIN_DIR); lrmd_log(LOG_ERR, "Startup aborted (no RA plugin). Shutting down."); exit(100); } while ( NULL != (subdir = readdir(dir))) { /* skip . and .. */ if ( '.' == subdir->d_name[0]) { continue; } /* skip the other type files */ if (NULL == strstr(subdir->d_name, ".so")) { continue; } /* remove the ".so" */ dot = strchr(subdir->d_name,'.'); if (NULL != dot) { len = (int)(dot - subdir->d_name); ra_name = g_strndup(subdir->d_name,len); } else { ra_name = g_strdup(subdir->d_name); } PILLoadPlugin(PluginLoadingSystem , "RAExec", ra_name, NULL); ra_class_list = g_list_append(ra_class_list,ra_name); } closedir(dir); dir = NULL; /* Don't forget to close 'dir' */ /* *create the waiting connections *one for register the client, *the other is for create the callback channel */ /*Create a waiting connection to accept command connect from client*/ conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(conn_cmd_attrs, path, cmd_path); conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs); g_hash_table_destroy(conn_cmd_attrs); if (NULL == conn_cmd) { lrmd_log(LOG_ERR, "main: can not create wait connection for command."); lrmd_log(LOG_ERR, "Startup aborted (can't create comm channel). Shutting down."); exit(100); } /*Create a source to handle new connect rquests for command*/ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE, on_connect_cmd, conn_cmd, NULL); /* auth is static, but used when clients register */ auth = ipc_str_to_auth(ADMIN_UIDS, strlen(ADMIN_UIDS), "", 0); /* * Create a waiting connection to accept the callback connect from client */ conn_cbk_attrs = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(conn_cbk_attrs, path, cbk_path); conn_cbk = ipc_wait_conn_constructor( IPC_ANYTYPE, conn_cbk_attrs); g_hash_table_destroy(conn_cbk_attrs); if (NULL == conn_cbk) { lrmd_log(LOG_ERR, "main: can not create wait connection for callback."); lrmd_log(LOG_ERR, "Startup aborted (can't create comm channel). Shutting down."); exit(100); } /*Create a source to handle new connect rquests for callback*/ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cbk, NULL, FALSE, on_connect_cbk, conn_cbk, NULL); /* our child signal handling involves calls with * unpredictable timing; so we raise the limit to * reduce the number of warnings */ set_sigchld_proctrack(G_PRIORITY_HIGH,10*DEFAULT_MAXDISPATCHTIME); lrmd_log(LOG_INFO, "enabling coredumps"); /* Although lrmd can count on the parent to enable coredump, still * set it here for test, when start manually. */ cl_cdtocoredir(); cl_enable_coredumps(TRUE); /* Allow us to always take a "secure" core dump * We might have STONITH logins and passwords, etc. in our address * space - so we need to make sure it's only readable by root. * Calling this function accomplishes that. */ cl_set_all_coredump_signal_handlers(); if( drop_privs(0, 0) ) { /* become "nobody" */ lrmd_log(LOG_WARNING,"%s: failed to drop privileges: %s" , __FUNCTION__, strerror(errno)); } /* * Add the signal handler for SIGUSR1, SIGUSR2. * They are used to change the debug level. */ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGUSR1, debug_level_adjust, NULL, NULL); G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGUSR2, debug_level_adjust, NULL, NULL); /* * alloc memory for client table and resource table */ clients = g_hash_table_new(g_int_hash, g_int_equal); if (clients == NULL) { cl_log(LOG_ERR, "can not new hash table clients"); exit(100); } resources = g_hash_table_new_full(g_str_hash , g_str_equal, free, NULL); if (resources == NULL) { cl_log(LOG_ERR, "can not new hash table resources"); exit(100); } /*Create the mainloop and run it*/ mainloop = g_main_new(FALSE); lrmd_debug(LOG_DEBUG, "main: run the loop..."); lrmd_log(LOG_INFO, "Started."); /* apphb initializing */ init_using_apphb(); emit_apphb(NULL); /* Avoid warning */ g_main_run(mainloop); emit_apphb(NULL); if (reg_to_apphbd == TRUE) { #ifdef ENABLE_APPHB apphb_unregister(); #endif reg_to_apphbd = FALSE; } if( return_to_orig_privs() ) { cl_perror("%s: failed to raise privileges", __FUNCTION__); } conn_cmd->ops->destroy(conn_cmd); conn_cmd = NULL; conn_cbk->ops->destroy(conn_cbk); conn_cbk = NULL; ipc_destroy_auth(auth); if (cl_unlock_pidfile(PID_FILE) == 0) { lrmd_debug(LOG_DEBUG, "[%s] stopped", lrm_system_name); } return 0; } /* *GLoop Message Handlers */ gboolean on_connect_cmd (IPC_Channel* ch, gpointer user_data) { lrmd_client_t* client = NULL; /* check paremeters */ if (NULL == ch) { lrmd_log(LOG_ERR, "on_connect_cmd: channel is null"); return TRUE; } /* create new client */ /* the register will be finished in on_msg_register */ client = lrmd_client_new(); if (client == NULL) { return TRUE; } client->app_name = NULL; client->ch_cmd = ch; client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, ch, FALSE, on_receive_cmd, (gpointer)client, on_remove_client); return TRUE; } gboolean on_connect_cbk (IPC_Channel* ch, gpointer user_data) { /*client connect for create the second channel for call back*/ pid_t pid; const char* type = NULL; struct ha_msg* msg = NULL; lrmd_client_t* client = NULL; if (NULL == ch) { lrmd_log(LOG_ERR, "on_connect_cbk: channel is null"); return TRUE; } /* Isn't this kind of a tight timing assumption ?? * This operation is non-blocking -- IIRC * Maybe this should be moved to the input dispatch function * for this channel when we make a GSource from it. * FIXME */ /*get the message, ends up in socket_waitin */ msg = msgfromIPC_noauth(ch); if (NULL == msg) { lrmd_log(LOG_ERR, "on_connect_cbk: can not receive msg"); return TRUE; } /*check if it is a register message*/ type = ha_msg_value(msg, F_LRM_TYPE); if (0 != STRNCMP_CONST(type, REGISTER)) { lrmd_log(LOG_ERR, "on_connect_cbk: received a message which is " "not known by lrmd."); ha_msg_del(msg); send_ret_msg(ch, HA_FAIL); return TRUE; } /*get the pid of client */ if (HA_OK != ha_msg_value_int(msg, F_LRM_PID, &pid)) { lrmd_log(LOG_ERR, "on_connect_cbk: can not get pid from the " "message."); ha_msg_del(msg); send_ret_msg(ch, HA_FAIL); return TRUE; } ha_msg_del(msg); /*get the client in the client list*/ client = lookup_client(pid); if (NULL == client) { lrmd_log(LOG_ERR, "on_connect_cbk: donnot find the client " "[pid:%d] in internal client list. ", pid); send_ret_msg(ch, HA_FAIL); return TRUE; } if (client->ch_cbk != NULL) { client->ch_cbk->ops->destroy(client->ch_cbk); client->ch_cbk = NULL; } client->g_src_cbk = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT , ch, FALSE,NULL,NULL,NULL); /*fill the channel of callback field*/ client->ch_cbk = ch; send_ret_msg(ch, HA_OK); return TRUE; } int msg_type_cmp(const void *p1, const void *p2) { return strncmp( ((const struct msg_map *)p1)->msg_type, ((const struct msg_map *)p2)->msg_type, MAX_MSGTYPELEN); } gboolean on_receive_cmd (IPC_Channel* ch, gpointer user_data) { struct msg_map *msgmap_p, in_type; lrmd_client_t* client = NULL; struct ha_msg* msg = NULL; char *msg_s; int ret; client = (lrmd_client_t*)user_data; if (IPC_DISCONNECT == ch->ch_status) { lrmd_debug(LOG_DEBUG, "on_receive_cmd: the IPC to client [pid:%d] disconnected." , client->pid); return FALSE; } if (!ch->ops->is_message_pending(ch)) { lrmd_debug(LOG_DEBUG, "on_receive_cmd: no pending message in IPC " "channel."); return TRUE; } /*get the message */ msg = msgfromIPC(ch, 0); if (NULL == msg) { lrmd_log(LOG_ERR, "on_receive_cmd: can not receive messages."); return TRUE; } if (TRUE == shutdown_in_progress ) { send_ret_msg(ch,HA_FAIL); ha_msg_del(msg); lrmd_log(LOG_INFO, "%s: new requests denied," \ " we're about to shutdown", __FUNCTION__); return TRUE; } /*dispatch the message*/ in_type.msg_type = ha_msg_value(msg, F_LRM_TYPE); if( !in_type.msg_type ) { LOG_FAILED_TO_GET_FIELD(F_LRM_TYPE); ha_msg_del(msg); return TRUE; } msg_s = msg2string(msg); if( msg_s ) { lrmd_debug2(LOG_DEBUG,"dumping request: %s",msg_s); free(msg_s); } if (!(msgmap_p = bsearch(&in_type, msg_maps, MSG_NR, sizeof(struct msg_map), msg_type_cmp) )) { lrmd_log(LOG_ERR, "on_receive_cmd: received an unknown msg"); } else { if( !client->app_name && msgmap_p->handler != on_msg_register ) { ha_msg_del(msg); lrmd_log(LOG_ERR, "%s: the client needs to register first", __FUNCTION__); return FALSE; } if( client->priv_lvl < msgmap_p->min_priv ) { ha_msg_del(msg); lrmd_log(LOG_ERR, "%s: insufficient privileges for %s (pid %d)" , __FUNCTION__ , client->app_name, client->pid); return FALSE; } strncpy(client->lastrequest, in_type.msg_type, sizeof(client->lastrequest)); client->lastrequest[sizeof(client->lastrequest)-1]='\0'; client->lastreqstart = time(NULL); /*call the handler of the message*/ ret = msgmap_p->handler(client, msg); client->lastreqend = time(NULL); /*return rc to client if need*/ if (send_msg_now(msgmap_p)) { send_ret_msg(ch, ret); client->lastrcsent = time(NULL); } } /*delete the msg*/ ha_msg_del(msg); return ret; } static void remove_repeat_op_from_client(gpointer key, gpointer value, gpointer user_data) { lrmd_rsc_t* rsc = (lrmd_rsc_t*)value; pid_t pid = GPOINTER_TO_UINT(user_data); /* pointer cast as int */ (void)flush_all(&(rsc->repeat_op_list),pid); } /* Remove all direct pointer references to 'client' before destroying it */ static int unregister_client(lrmd_client_t* client) { CHECK_ALLOCATED(client, "client", HA_FAIL); if (NULL == lookup_client(client->pid)) { lrmd_log(LOG_ERR,"%s: can not find client %s [pid %d] when try " "to unregister it." , __FUNCTION__ , client->app_name, client->pid); return HA_FAIL; } /* Search all resources for repeating ops this client owns */ g_hash_table_foreach(resources , remove_repeat_op_from_client, GUINT_TO_POINTER(client->pid)); /* Remove from clients */ g_hash_table_remove(clients, (gpointer)&client->pid); lrmd_debug(LOG_DEBUG, "%s: client %s [pid:%d] is unregistered" , __FUNCTION__ , client->app_name , client->pid); return HA_OK; } void on_remove_client (gpointer user_data) { lrmd_client_t* client = (lrmd_client_t*) user_data; CHECK_ALLOCATED(client, "client", ); if (client->g_src != NULL) { G_main_del_IPC_Channel(client->g_src); } if (client->g_src_cbk != NULL) { G_main_del_IPC_Channel(client->g_src_cbk); } lrmd_client_destroy(client); } /* This function called when its time to run a repeating operation now */ /* Move op from repeat queue to running queue */ gboolean on_repeat_op_readytorun(gpointer data) { lrmd_op_t* op = NULL; lrmd_rsc_t* rsc = NULL; LRMAUDIT(); op = (lrmd_op_t*)data; CHECK_ALLOCATED(op, "op", FALSE ); if (op->exec_pid == 0) { lrmd_log(LOG_ERR, "%s: exec_pid is 0 (internal error)" , __FUNCTION__); return FALSE; } lrmd_debug2(LOG_DEBUG , "%s: remove operation %s from the repeat operation list and " "add it to the operation list" , __FUNCTION__, op_info(op)); if( op->rsc_id ) { rsc = lookup_rsc(op->rsc_id); } else { lrmd_debug(LOG_INFO , "%s: the rsc_id in op %s is NULL" , __FUNCTION__, op_info(op)); return FALSE; } rsc->repeat_op_list = g_list_remove(rsc->repeat_op_list, op); if (op->repeat_timeout_tag != 0) { op->repeat_timeout_tag = (guint)0; } op->exec_pid = -1; if (!shutdown_in_progress) { add_op_to_runlist(rsc,op); } perform_op(rsc); LRMAUDIT(); return FALSE; } /*LRM Message Handlers*/ int on_msg_register(lrmd_client_t* client, struct ha_msg* msg) { lrmd_client_t* exist = NULL; const char* app_name = NULL; CHECK_ALLOCATED(msg, "register message", HA_FAIL); app_name = ha_msg_value(msg, F_LRM_APP); if (NULL == app_name) { lrmd_log(LOG_ERR, "on_msg_register: no app_name in " "the ha message."); return HA_FAIL; } client->app_name = strdup(app_name); return_on_no_int_value(msg, F_LRM_PID, &client->pid); return_on_no_int_value(msg, F_LRM_GID, (int *)&client->gid); return_on_no_int_value(msg, F_LRM_UID, (int *)&client->uid); exist = lookup_client(client->pid); if (NULL != exist) { g_hash_table_remove(clients, (gpointer)&client->pid); on_remove_client(exist); lrmd_log(LOG_NOTICE, "on_msg_register: the client [pid:%d] already exists in " "internal client list, let remove it at first." , client->pid); } /* everybody can connect, but only certain UIDs can perform * administrative actions */ if( client->ch_cmd->ops->verify_auth(client->ch_cmd, auth) == IPC_OK ) client->priv_lvl = PRIV_ADMIN; else client->priv_lvl = 0; g_hash_table_insert(clients, (gpointer)&client->pid, client); lrmd_debug(LOG_DEBUG, "on_msg_register:client %s [%d] registered" , client->app_name , client->pid); return HA_OK; } int on_msg_get_rsc_classes(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); lrmd_debug2(LOG_DEBUG , "on_msg_get_rsc_classes:client [%d] wants to get rsc classes" , client->pid); ret = create_lrm_ret(HA_OK, 4); CHECK_RETURN_OF_CREATE_LRM_RET; cl_msg_add_list(ret,F_LRM_RCLASS,ra_class_list); if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_rsc_classes: cannot send the ret mesage"); } ha_msg_del(ret); return HA_OK; } int on_msg_get_rsc_types(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; struct RAExecOps * RAExec = NULL; GList* types = NULL; GList* type; const char* rclass = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); ret = create_lrm_ret(HA_OK,5); CHECK_RETURN_OF_CREATE_LRM_RET; rclass = ha_msg_value(msg, F_LRM_RCLASS); if (rclass == NULL) { lrmd_log(LOG_ERR, "on_msg_get_rsc_types: cannot get the " "resource class field from the message."); send_ret_msg(client->ch_cmd, HA_FAIL); return HA_FAIL; } lrmd_debug2(LOG_DEBUG, "on_msg_get_rsc_types: the client [pid:%d] " "wants to get resource types of resource class %s" , client->pid, rclass); RAExec = g_hash_table_lookup(RAExecFuncs,rclass); if (NULL == RAExec) { lrmd_log(LOG_NOTICE, "on_msg_get_rsc_types: can not find this " "RA class %s.", rclass); } else { if (0 <= RAExec->get_resource_list(&types) && types != NULL) { cl_msg_add_list(ret, F_LRM_RTYPES, types); while (NULL != (type = g_list_first(types))) { types = g_list_remove_link(types, type); g_free(type->data); g_list_free_1(type); } g_list_free(types); } } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_rsc_types: can not send the ret message."); } ha_msg_del(ret); return HA_OK; } int on_msg_get_rsc_providers(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; struct RAExecOps * RAExec = NULL; GList* providers = NULL; GList* provider = NULL; const char* rclass = NULL; const char* rtype = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); ret = create_lrm_ret(HA_OK,5); CHECK_RETURN_OF_CREATE_LRM_RET; rclass = ha_msg_value(msg, F_LRM_RCLASS); rtype = ha_msg_value(msg, F_LRM_RTYPE); if( !rclass || !rtype ) { lrmd_log(LOG_NOTICE , "%s: could not retrieve resource class or type" , __FUNCTION__); send_ret_msg(client->ch_cmd, HA_FAIL); return HA_FAIL; } lrmd_debug2(LOG_DEBUG , "%s: the client [%d] wants to get rsc privider of %s::%s" , __FUNCTION__ , client->pid , rclass , rtype); RAExec = g_hash_table_lookup(RAExecFuncs, rclass); if (NULL == RAExec) { lrmd_log(LOG_NOTICE , "%s: can not find the class %s." , __FUNCTION__ , rclass); } else { if (0 <= RAExec->get_provider_list(rtype, &providers)) { if (providers != NULL) { cl_msg_add_list(ret, F_LRM_RPROVIDERS, providers); } while (NULL != (provider = g_list_first(providers))) { providers = g_list_remove_link(providers, provider); g_free(provider->data); g_list_free_1(provider); } g_list_free(providers); } } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_rsc_providers: can not send the ret msg"); } ha_msg_del(ret); return HA_OK; } int on_msg_get_metadata(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; struct RAExecOps * RAExec = NULL; const char* rtype = NULL; const char* rclass = NULL; const char* provider = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); rtype = ha_msg_value(msg, F_LRM_RTYPE); rclass = ha_msg_value(msg, F_LRM_RCLASS); provider = ha_msg_value(msg, F_LRM_RPROVIDER); lrmd_debug2(LOG_DEBUG , "%s: the client [pid:%d] wants to get rsc metadata of %s::%s::%s." , __FUNCTION__ , client->pid , lrm_str(rclass) , lrm_str(provider) , lrm_str(rtype)); ret = create_lrm_ret(HA_OK, 5); CHECK_RETURN_OF_CREATE_LRM_RET; RAExec = g_hash_table_lookup(RAExecFuncs,rclass); if (NULL == RAExec) { lrmd_log(LOG_NOTICE , "%s: can not find the class %s." , __FUNCTION__ , rclass); } else { char* meta = RAExec->get_resource_meta(rtype,provider); if (NULL != meta && strlen(meta) > 0) { if (HA_OK != ha_msg_add(ret,F_LRM_METADATA, meta)) { LOG_FAILED_TO_ADD_FIELD("metadata"); } g_free(meta); } else { lrmd_log(LOG_WARNING , "%s: empty metadata for %s::%s::%s." , __FUNCTION__ , lrm_str(rclass) , lrm_str(provider) , lrm_str(rtype)); ha_msg_mod_int(ret, F_LRM_RET, HA_FAIL); } } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_metadata: can not send the ret msg"); } ha_msg_del(ret); return HA_OK; } static void add_rid_to_msg(gpointer key, gpointer value, gpointer user_data) { char* rid = (char*)key; struct ha_msg* msg = (struct ha_msg*)user_data; if (HA_OK != cl_msg_list_add_string(msg,F_LRM_RID,rid)) { LOG_FAILED_TO_ADD_FIELD("resource id"); } } int on_msg_get_all(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); lrmd_debug2(LOG_DEBUG , "on_msg_get_all:client [%d] want to get all rsc information." , client->pid); ret = create_lrm_ret(HA_OK, g_hash_table_size(resources) + 1); CHECK_RETURN_OF_CREATE_LRM_RET; g_hash_table_foreach(resources, add_rid_to_msg, ret); if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_all: can not send the ret msg"); } ha_msg_del(ret); return HA_OK; } int on_msg_get_rsc(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; lrmd_rsc_t* rsc = NULL; const char* id = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); id = ha_msg_value(msg, F_LRM_RID); lrmd_debug2(LOG_DEBUG , "on_msg_get_rsc: the client [pid:%d] wants to get " "the information of the resource [rsc_id: %s]" , client->pid, lrmd_nullcheck(id)); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_debug2(LOG_DEBUG , "on_msg_get_rsc: no rsc with id %s." , lrmd_nullcheck(id)); ret = create_lrm_ret(HA_FAIL, 1); CHECK_RETURN_OF_CREATE_LRM_RET; } else { ret = create_lrm_ret(HA_OK, 5); CHECK_RETURN_OF_CREATE_LRM_RET; if (HA_OK != ha_msg_add(ret, F_LRM_RID, rsc->id) || HA_OK != ha_msg_add(ret, F_LRM_RTYPE, rsc->type) || HA_OK != ha_msg_add(ret, F_LRM_RCLASS, rsc->class)) { ha_msg_del(ret); lrmd_log(LOG_ERR, "on_msg_get_rsc: failed to add fields to msg."); return HA_FAIL; } if( rsc->provider ) { if (HA_OK != ha_msg_add(ret, F_LRM_RPROVIDER, rsc->provider)) { ha_msg_del(ret); LOG_FAILED_TO_ADD_FIELD("provider"); return HA_FAIL; } } if ( rsc->params && HA_OK!=ha_msg_add_str_table(ret,F_LRM_PARAM,rsc->params)) { ha_msg_del(ret); LOG_FAILED_TO_ADD_FIELD("parameter"); return HA_FAIL; } } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_rsc: can not send the ret msg"); } ha_msg_del(ret); return HA_OK; } int on_msg_get_last_op(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; const char* op_type = NULL; lrmd_rsc_t* rsc = NULL; const char* rid = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); rid = ha_msg_value(msg, F_LRM_RID); op_type = ha_msg_value(msg, F_LRM_OP); lrmd_debug2(LOG_DEBUG , "on_msg_get_last_op:client %s[%d] want to get the information " "regarding last %s op on %s" , client->app_name, client->pid , lrmd_nullcheck(op_type), lrmd_nullcheck(rid)); rsc = lookup_rsc_by_msg(msg); if (NULL != rsc && NULL != op_type) { GHashTable* table = g_hash_table_lookup(rsc->last_op_table , client->app_name); if (NULL != table ) { lrmd_op_t* op = g_hash_table_lookup(table, op_type); if (NULL != op) { lrmd_debug(LOG_DEBUG , "%s: will return op %s" , __FUNCTION__ , op_type); ret = op_to_msg(op); if (NULL == ret) { lrmd_log(LOG_ERR , "%s: can't create a message with op_to_msg." , __FUNCTION__); } else if (HA_OK != ha_msg_add_int(ret , F_LRM_OPCNT, 1)) { LOG_FAILED_TO_ADD_FIELD("operation count"); } } } } if (NULL == ret) { lrmd_log(LOG_ERR , "%s: return ha_msg ret is null, will re-create it again." , __FUNCTION__); ret = create_lrm_ret(HA_OK, 1); CHECK_RETURN_OF_CREATE_LRM_RET; if (HA_OK != ha_msg_add_int(ret, F_LRM_OPCNT, 0)) { LOG_FAILED_TO_ADD_FIELD("operation count"); } } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_last_op: can not send the ret msg"); } ha_msg_del(ret); return HA_OK; } int on_msg_del_rsc(lrmd_client_t* client, struct ha_msg* msg) { lrmd_rsc_t* rsc = NULL; const char* id = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); id = ha_msg_value(msg, F_LRM_RID); lrmd_debug2(LOG_DEBUG , "%s: client [%d] wants to delete rsc %s" , __FUNCTION__, client->pid, lrmd_nullcheck(id)); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_log(LOG_ERR, "%s: no rsc with id %s.",__FUNCTION__,id); return -1; } LRMAUDIT(); (void)flush_all(&(rsc->repeat_op_list),0); if( flush_all(&(rsc->op_list),0) ) { set_rsc_removal_pending(rsc); lrmd_log(LOG_INFO, "resource %s busy, removal pending", rsc->id); LRMAUDIT(); return HA_RSCBUSY; /* resource is busy, removal delayed */ } lrmd_rsc_destroy(rsc); LRMAUDIT(); return HA_OK; } static int prepare_failmsg(struct ha_msg* msg, int fail_rc, const char *fail_reason) { call_id++; /* use the next id */ if (HA_OK != ha_msg_mod(msg,F_LRM_OP,ASYNC_OP_NAME) || HA_OK != ha_msg_add(msg,F_LRM_FAIL_REASON,fail_reason) || HA_OK != ha_msg_mod_int(msg,F_LRM_ASYNCMON_RC,fail_rc) || HA_OK != ha_msg_mod_int(msg,F_LRM_RC,fail_rc) || HA_OK != ha_msg_mod_int(msg,F_LRM_OPSTATUS,(int)LRM_OP_DONE) || HA_OK != ha_msg_mod_int(msg,F_LRM_CALLID,call_id) || HA_OK != ha_msg_mod_int(msg,F_LRM_TIMEOUT,0) || HA_OK != ha_msg_mod_int(msg,F_LRM_INTERVAL,0) || HA_OK != ha_msg_mod_int(msg,F_LRM_TARGETRC,EVERYTIME) || HA_OK != ha_msg_mod_int(msg,F_LRM_DELAY,0) ) { lrmd_log(LOG_ERR,"%s:%d: cannot add field to a message" , __FUNCTION__, __LINE__); return 1; } return 0; } static void async_notify(gpointer key, gpointer val, gpointer data) { struct ha_msg* msg = (struct ha_msg*)data; lrmd_client_t* client; client = lookup_client_by_name((char *)key); if (!client) { lrmd_log(LOG_INFO, "%s: client %s not found, probably signed out", __FUNCTION__, (char *)key); } else { send_msg(msg, client); } } int on_msg_fail_rsc(lrmd_client_t* client, struct ha_msg* msg) { lrmd_rsc_t* rsc; const char* id; int fail_rc = -1; const char *fail_reason; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); id = ha_msg_value(msg, F_LRM_RID); lrmd_debug2(LOG_DEBUG , "%s: client [%d] wants to fail rsc %s" , __FUNCTION__, client->pid, lrmd_nullcheck(id)); rsc = lookup_rsc_by_msg(msg); if (!rsc) { lrmd_log(LOG_ERR, "%s: no resource with id %s." , __FUNCTION__, lrmd_nullcheck(id)); return HA_FAIL; } fail_reason = ha_msg_value(msg,F_LRM_FAIL_REASON); if (!fail_reason || *fail_reason == '\0') { fail_reason = DEFAULT_FAIL_REASON; } if (HA_OK != ha_msg_value_int(msg,F_LRM_ASYNCMON_RC,&fail_rc) || fail_rc <= 0) { fail_rc = DEFAULT_FAIL_RC; } if (prepare_failmsg(msg,fail_rc,fail_reason)) return HA_FAIL; lrmd_log(LOG_WARNING , "received asynchronous failure for rsc %s (rc: %d, reason: %s)" , lrmd_nullcheck(id), fail_rc, fail_reason); /* notify all clients from last_op table about the failure */ if (rsc->last_op_table) { g_hash_table_foreach(rsc->last_op_table,async_notify,msg); } else { lrmd_log(LOG_INFO , "rsc to be failed %s had no operations so far", lrmd_nullcheck(id)); send_msg(msg, client); } return HA_OK; } static gboolean free_str_hash_pair(gpointer key, gpointer value, gpointer user_data) { GHashTable* table = (GHashTable*) value; free(key); g_hash_table_foreach_remove(table, free_str_op_pair, NULL); g_hash_table_destroy(table); return TRUE; } static gboolean free_str_op_pair(gpointer key, gpointer value, gpointer user_data) { lrmd_op_t* op = (lrmd_op_t*)value; if (NULL == op) { lrmd_log(LOG_ERR, "%s(): NULL op in op_pair(%s)" , __FUNCTION__ , (const char *)key); }else{ lrmd_op_destroy(op); } return TRUE; } int on_msg_add_rsc(lrmd_client_t* client, struct ha_msg* msg) { GList* node; gboolean ra_type_exist = FALSE; char* class = NULL; lrmd_rsc_t* rsc = NULL; const char* id = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); return_on_no_value(msg, F_LRM_RID,id); lrmd_debug(LOG_DEBUG , "on_msg_add_rsc:client [%d] adds resource %s" , client->pid, lrmd_nullcheck(id)); if (RID_LEN <= strlen(id)) { lrmd_log(LOG_ERR, "on_msg_add_rsc: rsc_id is too long."); return HA_FAIL; } if (NULL != lookup_rsc(id)) { lrmd_log(LOG_ERR, "on_msg_add_rsc: same id resource exists."); return HA_FAIL; } LRMAUDIT(); rsc = lrmd_rsc_new(id, msg); if (rsc == NULL) { return HA_FAIL; } ra_type_exist = FALSE; for(node=g_list_first(ra_class_list); NULL!=node; node=g_list_next(node)){ class = (char*)node->data; if (0 == strncmp(class, rsc->class, MAX_CLASSNAMELEN)) { ra_type_exist = TRUE; break; } } if (!ra_type_exist) { lrmd_log(LOG_ERR , "on_msg_add_rsc: RA class [%s] does not exist." , rsc->class); lrmd_rsc_destroy(rsc); rsc = NULL; LRMAUDIT(); return HA_FAIL; } rsc->last_op_done = NULL; rsc->params = ha_msg_value_str_table(msg,F_LRM_PARAM); rsc->last_op_table = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(resources, strdup(rsc->id), rsc); LRMAUDIT(); return HA_OK; } static int cancel_op(GList** listp,int cancel_op_id) { GList* node = NULL; lrmd_op_t* op = NULL; int rc = HA_FAIL; for( node = g_list_first(*listp) ; node; node = g_list_next(node) ) { op = (lrmd_op_t*)node->data; if( op->call_id == cancel_op_id ) { lrmd_log(LOG_INFO ,"%s: %s cancelled" , __FUNCTION__, op_info(op)); rc = flush_op(op); if( rc != HA_RSCBUSY && rc != HA_FAIL ) { notify_client(op); /* send notification now */ *listp = g_list_remove(*listp, op); remove_op_history(op); lrmd_op_destroy(op); } return rc; } } return rc; } int on_msg_cancel_op(lrmd_client_t* client, struct ha_msg* msg) { lrmd_rsc_t* rsc = NULL; int cancel_op_id = 0; int op_cancelled = HA_OK; LRMAUDIT(); CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_log(LOG_ERR, "%s: no resource with such id.", __FUNCTION__); return HA_FAIL; } return_on_no_int_value(msg, F_LRM_CALLID, &cancel_op_id); lrmd_debug2(LOG_DEBUG , "%s:client [pid:%d] cancel the operation [callid:%d]" , __FUNCTION__ , client->pid , cancel_op_id); if( cancel_op(&(rsc->repeat_op_list), cancel_op_id) != HA_OK ) { op_cancelled = cancel_op(&(rsc->op_list), cancel_op_id); } if( op_cancelled == HA_FAIL ) { lrmd_log(LOG_INFO, "%s: no operation with id %d", __FUNCTION__, cancel_op_id); } else if( op_cancelled == HA_RSCBUSY ) { lrmd_log(LOG_INFO, "%s: operation %d running, cancel pending", __FUNCTION__, cancel_op_id); } else { lrmd_debug(LOG_DEBUG, "%s: operation %d cancelled", __FUNCTION__, cancel_op_id); } LRMAUDIT(); return op_cancelled; } static gboolean flush_all(GList** listp, int client_pid) { GList* node = NULL; lrmd_op_t* op = NULL; gboolean rsc_busy = FALSE; node = g_list_first(*listp); while( node ) { op = (lrmd_op_t*)node->data; if (client_pid && op->client_id != client_pid) { node = g_list_next(node); continue; /* not the client's operation */ } if( flush_op(op) == HA_RSCBUSY ) { rsc_busy = TRUE; node = g_list_next(node); } else if (!client_pid || op->client_id == client_pid) { node = *listp = g_list_remove(*listp, op); remove_op_history(op); lrmd_op_destroy(op); } else { node = g_list_next(node); } } return rsc_busy; } int on_msg_flush_all(lrmd_client_t* client, struct ha_msg* msg) { lrmd_rsc_t* rsc = NULL; const char* id = NULL; LRMAUDIT(); CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); return_on_no_value(msg, F_LRM_RID,id); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_log(LOG_ERR, "%s: no resource with id %s.", __FUNCTION__,id); LRMAUDIT(); return -1; } /* when a flush request arrived, flush all pending ops */ lrmd_debug2(LOG_DEBUG , "%s:client [%d] flush operations" , __FUNCTION__, client->pid); (void)flush_all(&(rsc->repeat_op_list),0); if( flush_all(&(rsc->op_list),0) ) { set_rsc_flushing_ops(rsc); /* resource busy */ lrmd_log(LOG_INFO, "resource %s busy, all flush pending", rsc->id); LRMAUDIT(); return HA_RSCBUSY; } LRMAUDIT(); return HA_OK; } int on_msg_perform_op(lrmd_client_t* client, struct ha_msg* msg) { lrmd_rsc_t* rsc = NULL; lrmd_op_t* op; const char* id = NULL; int timeout = 0; int interval = 0; int delay = 0; LRMAUDIT(); CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); return_on_no_value(msg, F_LRM_RID,id); return_on_no_int_value(msg, F_LRM_INTERVAL, &interval); return_on_no_int_value(msg, F_LRM_TIMEOUT, &timeout); return_on_no_int_value(msg, F_LRM_DELAY, &delay); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_log(LOG_ERR, "%s: no resource with such id.", __FUNCTION__); return -1; } if( rsc_frozen(rsc) ) { lrmd_log(LOG_NOTICE, "%s: resource %s is frozen, " "no ops can run.", __FUNCTION__, rsc->id); return -1; } call_id++; if( !(rsc->id) ) { lrmd_debug(LOG_ERR , "%s:%d: the resource id is NULL" , __FUNCTION__, __LINE__); return -1; } if (HA_OK != ha_msg_add_int(msg, F_LRM_CALLID, call_id)) { LOG_FAILED_TO_ADD_FIELD("callid"); return -1; } if (HA_OK !=ha_msg_mod(msg, F_LRM_APP, client->app_name)) { LOG_FAILED_TO_ADD_FIELD("app_name"); return -1; } op = lrmd_op_new(); if (op == NULL) { return -1; } op->call_id = call_id; op->client_id = client->pid; op->rsc_id = strdup(rsc->id); op->interval = interval; op->delay = delay; op->weight = no_child_count(rsc) ? 0 : 1; op->msg = ha_msg_copy(msg); if( ha_msg_value_int(msg,F_LRM_COPYPARAMS,&op->copyparams) == HA_OK && op->copyparams ) { lrmd_debug(LOG_DEBUG , "%s:%d: copying parameters for rsc %s" , __FUNCTION__, __LINE__,rsc->id); if (rsc->params) { free_str_table(rsc->params); } rsc->params = ha_msg_value_str_table(msg, F_LRM_PARAM); } lrmd_debug2(LOG_DEBUG , "%s: client [%d] want to add an operation %s on resource %s." , __FUNCTION__ , client->pid , op_info(op) , NULL!=op->rsc_id ? op->rsc_id : "#EMPTY#"); if ( 0 < op->delay ) { op->repeat_timeout_tag = Gmain_timeout_add(op->delay ,on_repeat_op_readytorun, op); rsc->repeat_op_list = g_list_append (rsc->repeat_op_list, op); lrmd_debug(LOG_DEBUG , "%s: an operation %s is added to the repeat " "operation list for delay execution" , __FUNCTION__ , op_info(op)); } else { lrmd_debug(LOG_DEBUG , "%s: add an operation %s to the operation list." , __FUNCTION__ , op_info(op)); add_op_to_runlist(rsc,op); } perform_op(rsc); LRMAUDIT(); return call_id; } static void send_last_op(gpointer key, gpointer value, gpointer user_data) { IPC_Channel* ch = NULL; lrmd_op_t* op = NULL; struct ha_msg* msg = NULL; ch = (IPC_Channel*)user_data; op = (lrmd_op_t*)value; msg = op_to_msg(op); if (msg == NULL) { lrmd_log(LOG_ERR, "send_last_op: failed to convert an operation " "information to a ha_msg."); return; } if (HA_OK != msg2ipcchan(msg, ch)) { lrmd_log(LOG_ERR, "send_last_op: can not send a message."); } ha_msg_del(msg); } int on_msg_get_state(lrmd_client_t* client, struct ha_msg* msg) { int op_count = 0; lrmd_rsc_t* rsc = NULL; GList* node; struct ha_msg* ret = NULL; lrmd_op_t* op = NULL; struct ha_msg* op_msg = NULL; const char* id = NULL; GHashTable* last_ops = NULL; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); id = ha_msg_value(msg,F_LRM_RID); lrmd_debug2(LOG_DEBUG , "%s: client [%d] want to get the state of resource %s" , __FUNCTION__, client->pid, lrmd_nullcheck(id)); rsc = lookup_rsc_by_msg(msg); if (NULL == rsc) { lrmd_log(LOG_ERR, "on_msg_get_state: no resource with id %s." , lrmd_nullcheck(id)); send_ret_msg(client->ch_cmd, HA_FAIL); return HA_FAIL; } ret = ha_msg_new(5); if (NULL == ret) { lrmd_log(LOG_ERR, "on_msg_get_state: can't create a ha_msg."); return HA_FAIL; } /* add the F_LRM_STATE field */ if (HA_OK != ha_msg_add_int(ret, F_LRM_STATE , rsc->op_list ? LRM_RSC_BUSY : LRM_RSC_IDLE)) { LOG_FAILED_TO_ADD_FIELD("state"); ha_msg_del(ret); return HA_FAIL; } lrmd_debug(LOG_DEBUG , "on_msg_get_state:state of rsc %s is %s" , lrmd_nullcheck(id) , rsc->op_list ? "LRM_RSC_BUSY" : "LRM_RSC_IDLE" ); /* calculate the count of ops being returned */ last_ops = g_hash_table_lookup(rsc->last_op_table, client->app_name); if (last_ops == NULL) { op_count = g_list_length(rsc->op_list) + g_list_length(rsc->repeat_op_list); } else { op_count = g_hash_table_size(last_ops) + g_list_length(rsc->op_list) + g_list_length(rsc->repeat_op_list); } /* add the count of ops being returned */ if (HA_OK != ha_msg_add_int(ret, F_LRM_OPCNT, op_count)) { LOG_FAILED_TO_ADD_FIELD("operation count"); ha_msg_del(ret); return HA_FAIL; } /* send the first message to client */ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_state: can not send the ret message."); ha_msg_del(ret); return HA_FAIL; } ha_msg_del(ret); /* send the ops in last ops table */ if(last_ops != NULL) { g_hash_table_foreach(last_ops, send_last_op, client->ch_cmd); } /* send the ops in op list */ for(node = g_list_first(rsc->op_list) ; NULL != node; node = g_list_next(node)){ op = (lrmd_op_t*)node->data; op_msg = op_to_msg(op); if (NULL == op_msg) { lrmd_log(LOG_ERR, "on_msg_get_state: failed to make a message " "from a operation: %s", op_info(op)); continue; } if (HA_OK != msg2ipcchan(op_msg, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_state: failed to send a message."); } ha_msg_del(op_msg); } /* send the ops in repeat op list */ for(node = g_list_first(rsc->repeat_op_list) ; NULL != node; node = g_list_next(node)){ op = (lrmd_op_t*)node->data; op_msg = op_to_msg(op); if (NULL == op_msg) { lrmd_log(LOG_ERR, "on_msg_get_state: failed to make a message " "from a operation: %s", op_info(op)); continue; } if (HA_OK != msg2ipcchan(op_msg, client->ch_cmd)) { lrmd_log(LOG_ERR, "on_msg_get_state: failed to send a message."); } ha_msg_del(op_msg); } return HA_OK; } #define safe_len(s) (s ? strlen(s) : 0) static char * lrm_concat(const char *prefix, const char *suffix, char join) { int len = 2; char *new_str = NULL; len += safe_len(prefix); len += safe_len(suffix); new_str = malloc(sizeof(char)*len); if (NULL == new_str) { lrmd_log(LOG_ERR,"%s:%d: malloc failed" , __FUNCTION__, __LINE__); return NULL; } memset(new_str, 0, len); sprintf(new_str, "%s%c%s", prefix?prefix:"", join, suffix?suffix:""); new_str[len-1] = 0; return new_str; } /* /////////////////////op functions////////////////////// */ #define mk_op_id(op,id) do { \ const char *op_type = ha_msg_value(op->msg, F_LRM_OP); \ const char *op_interval = ha_msg_value(op->msg, F_LRM_INTERVAL); \ id = lrm_concat(op_type, op_interval, '_'); \ } while(0) /* find the last operation for the client * replace it with the new one (if requested) */ static void replace_last_op(lrmd_client_t* client, lrmd_rsc_t* rsc, lrmd_op_t* op) { char *op_hash_key; GHashTable *client_last_op; lrmd_op_t *old_op, *new_op; if (!client || !rsc || !op) return; client_last_op = g_hash_table_lookup(rsc->last_op_table, client->app_name); if (!client_last_op) { lrmd_debug2(LOG_DEBUG , "%s: new last op table for client %s" , __FUNCTION__, client->app_name); client_last_op = g_hash_table_new_full( g_str_hash , g_str_equal, free, NULL); g_hash_table_insert(rsc->last_op_table , (gpointer)strdup(client->app_name) , (gpointer)client_last_op); } mk_op_id(op,op_hash_key); old_op = (lrmd_op_t*)g_hash_table_lookup(client_last_op, op_hash_key); /* make a copy of op and insert it into client_last_op */ if (!(new_op = lrmd_op_copy(op))) { lrmd_log(LOG_ERR, "%s:%d out of memory" , __FUNCTION__, __LINE__); } if (old_op) { lrmd_debug2(LOG_DEBUG , "%s: replace last op %s for client %s" , __FUNCTION__, op_hash_key, client->app_name); g_hash_table_replace(client_last_op,op_hash_key,(gpointer)new_op); lrmd_op_destroy(old_op); } else { lrmd_debug2(LOG_DEBUG , "%s: add last op %s for client %s" , __FUNCTION__, op_hash_key, client->app_name); g_hash_table_insert(client_last_op,op_hash_key,(gpointer)new_op); } } static int record_op_completion(lrmd_rsc_t* rsc, lrmd_op_t* op) { lrmd_client_t* client; LRMAUDIT(); /*save the op in the last op finished*/ if (rsc->last_op_done != NULL) { lrmd_op_destroy(rsc->last_op_done); } if (!(rsc->last_op_done = lrmd_op_copy(op))) { lrmd_log(LOG_ERR, "%s:%d out of memory" , __FUNCTION__, __LINE__); return 1; } rsc->last_op_done->repeat_timeout_tag = (guint)0; client = lookup_client(op->client_id); if (!client) { lrmd_log(LOG_INFO, "%s: cannot record %s: the client is gone" , __FUNCTION__, small_op_info(op)); LRMAUDIT(); return 1; } /* insert (or replace) the new op in last_op_table for the client */ replace_last_op(client,rsc,op); LRMAUDIT(); return 0; } static void to_repeatlist(lrmd_rsc_t* rsc, lrmd_op_t* op) { lrmd_op_t *repeat_op; if (!(repeat_op = lrmd_op_copy(op))) { lrmd_log(LOG_ERR, "%s:%d out of memory" , __FUNCTION__, __LINE__); } reset_timestamps(repeat_op); repeat_op->is_copy = FALSE; repeat_op->repeat_timeout_tag = Gmain_timeout_add(op->interval, on_repeat_op_readytorun, repeat_op); rsc->repeat_op_list = g_list_append (rsc->repeat_op_list, repeat_op); lrmd_debug2(LOG_DEBUG , "%s: repeat %s is added to repeat op list to wait" , __FUNCTION__, op_info(op)); } static void remove_op_history(lrmd_op_t* op) { lrmd_client_t* client = lookup_client(op->client_id); lrmd_rsc_t* rsc = NULL; char *op_id, *last_op_id; lrmd_op_t* old_op = NULL; GHashTable* client_last_op = NULL; LRMAUDIT(); if( !(rsc = lookup_rsc(op->rsc_id)) ) { return; } lrmd_debug2(LOG_DEBUG, "%s: remove history of the op %s" ,__FUNCTION__, op_info(op)); mk_op_id(op,op_id); if (rsc->last_op_done != NULL ) { mk_op_id(rsc->last_op_done,last_op_id); if( !strcmp(op_id,last_op_id) ) { lrmd_debug2(LOG_DEBUG, "%s: remove history of the last op done %s" ,__FUNCTION__, op_info(rsc->last_op_done)); lrmd_op_destroy(rsc->last_op_done); rsc->last_op_done = NULL; } free(last_op_id); } if( client && (client_last_op = g_hash_table_lookup(rsc->last_op_table , client->app_name)) ) { lrmd_debug2(LOG_DEBUG, "%s: found client %s in the last op table" ,__FUNCTION__, client->app_name); old_op = g_hash_table_lookup(client_last_op, op_id); if (old_op) { g_hash_table_remove(client_last_op, op_id); lrmd_debug2(LOG_DEBUG, "%s: remove history of the client's last %s" ,__FUNCTION__, op_info(old_op)); lrmd_op_destroy(old_op); } } free(op_id); LRMAUDIT(); } static void add_op_to_runlist(lrmd_rsc_t* rsc, lrmd_op_t* op) { op->t_addtolist = time_longclock(); rsc->op_list = g_list_append(rsc->op_list, op); if (g_list_length(rsc->op_list) >= 4) { lrmd_log(LOG_WARNING , "operations list for %s is suspiciously" " long [%d]" , rsc->id , g_list_length(rsc->op_list)); lrmd_rsc_dump(rsc->id, "rsc->op_list: too many ops"); } } /* 1. this function sends a message to the client: * a) on operation instance exit using the callback channel * b) in case a client requested that operation to be cancelled, * using the command channel * c) in case a client requested a resource removal or flushing * all ops and this is the last operation that finished, again * using the command channel * 2. if the op was not cancelled: * a) it is copied to the last_op_done field of rsc * b) if it's a repeating op, it is put in the repeat_op_list * c) the outcome is recorded for future reference * 3. op is destroyed and removed from the op_list */ int on_op_done(lrmd_rsc_t* rsc, lrmd_op_t* op) { int rc = HA_OK; int target_rc, last_rc, op_rc; int rc_changed; op_status_t op_status; LRMAUDIT(); CHECK_ALLOCATED(op, "op", HA_FAIL ); if (op->exec_pid == 0) { lrmd_log(LOG_ERR, "%s: op->exec_pid == 0",__FUNCTION__); return HA_FAIL; } op->t_done = time_longclock(); if (debug_level >= 2) { lrmd_debug(LOG_DEBUG, "%s: %s",__FUNCTION__, op_info(op)); lrmd_op_dump(op, __FUNCTION__); } return_on_no_int_value(op->msg,F_LRM_TARGETRC,&target_rc); return_on_no_int_value(op->msg,F_LRM_OPSTATUS,(int *)&op_status); last_rc = op_rc = -1; /* set all rc to -1 */ ha_msg_value_int(op->msg,F_LRM_RC,&op_rc); ha_msg_value_int(op->msg,F_LRM_LASTRC,&last_rc); rc_changed = ( op_status == LRM_OP_DONE && op_rc != -1 && ((last_rc == -1) || (last_rc != op_rc)) ); if (rc_changed) { if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_LASTRC, op_rc)) { lrmd_log(LOG_ERR,"%s: cannot save status to msg",__FUNCTION__); return HA_FAIL; } op->t_rcchange = op->t_perform; } if (store_timestamps(op)) return HA_FAIL; /* remove the op from op_list */ rsc->op_list = g_list_remove(rsc->op_list,op); lrmd_debug2(LOG_DEBUG , "%s:%s is removed from op list" , __FUNCTION__, op_info(op)); if (!op->is_cancelled) { if( !record_op_completion(rsc,op) ) { /*record the outcome of the op */ if (op->interval) /* copy op to the repeat list */ to_repeatlist(rsc,op); } } else { if (HA_OK != ha_msg_mod_int(op->msg,F_LRM_OPSTATUS,(int)LRM_OP_CANCELLED)) { LOG_FAILED_TO_ADD_FIELD(F_LRM_OPSTATUS); return HA_FAIL; } op_status = LRM_OP_CANCELLED; remove_op_history(op); } if (rsc_removal_pending(rsc)) { if (HA_OK != ha_msg_add_int(op->msg,F_LRM_RSCDELETED,1)) { LOG_FAILED_TO_ADD_FIELD(F_LRM_RSCDELETED); } } if (op_status != LRM_OP_DONE || (op_rc == -1) || (op_rc == target_rc) || (target_rc == EVERYTIME) || ((target_rc == CHANGED) && rc_changed) || rsc_removal_pending(rsc) ) { notify_client(op); } lrmd_op_destroy(op); if( !rsc->op_list ) { if( rsc_removal_pending(rsc) ) { lrmd_log(LOG_INFO, "late removal of resource %s", rsc->id); lrmd_rsc_destroy(rsc); rc = -1; /* let the caller know that the rsc is gone */ } else { rsc_reset_state(rsc); } } LRMAUDIT(); if (shutdown_in_progress && can_shutdown()) { lrm_shutdown(); } return rc; } /* * an operation is flushed only in case there is * no process running initiated by this operation * NB: the caller has to destroy the operation itself */ int flush_op(lrmd_op_t* op) { CHECK_ALLOCATED(op, "op", HA_FAIL ); if (op->exec_pid == 0) { lrmd_debug(LOG_ERR, "%s: op->exec_pid == 0",__FUNCTION__); return HA_FAIL; } if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_RC, HA_FAIL)) { LOG_FAILED_TO_ADD_FIELD("F_LRM_RC"); return HA_FAIL; } if( op->exec_pid == -1 ) { if (HA_OK != ha_msg_mod_int(op->msg,F_LRM_OPSTATUS,(int)LRM_OP_CANCELLED)){ LOG_FAILED_TO_ADD_FIELD("opstatus"); return HA_FAIL; } return HA_OK; } else { op->is_cancelled = TRUE; /* mark the op as cancelled */ lrmd_log(LOG_INFO, "%s: process for %s still " "running, flush delayed" ,__FUNCTION__,small_op_info(op)); return HA_RSCBUSY; } } /* Resume the execution of ops of the resource */ static gboolean rsc_execution_freeze_timeout(gpointer data) { lrmd_rsc_t* rsc = (lrmd_rsc_t*)data; if (rsc == NULL) { return FALSE; } if (rsc->delay_timeout > 0) { rsc->delay_timeout = (guint)0; } perform_op(rsc); return FALSE; } /* this function gets the first op in the rsc op list and execute it*/ int perform_op(lrmd_rsc_t* rsc) { GList* node = NULL; lrmd_op_t* op = NULL; LRMAUDIT(); CHECK_ALLOCATED(rsc, "resource", HA_FAIL); if (shutdown_in_progress && can_shutdown()) { lrm_shutdown(); } if (rsc_frozen(rsc)) { lrmd_log(LOG_INFO,"%s: resource %s is frozen, " "no ops allowed to run" , __FUNCTION__, rsc->id); return HA_OK; } if (NULL == rsc->op_list) { lrmd_debug2(LOG_DEBUG,"%s: no op to perform?", __FUNCTION__); return HA_OK; } node = g_list_first(rsc->op_list); while (NULL != node) { op = node->data; if (-1 != op->exec_pid) { if (!g_list_next(node)) { /* this is the only operation, no need to do * anything further */ break; } lrmd_log(LOG_INFO, "%s:%d: %s for rsc is already running." , __FUNCTION__, __LINE__, op_info(op)); if( rsc->delay_timeout > 0 ) { lrmd_log(LOG_INFO , "%s:%d: operations on resource %s already delayed" , __FUNCTION__, __LINE__, lrm_str(rsc->id)); } else { lrmd_log(LOG_INFO , "%s:%d: postponing " "all ops on resource %s by %d ms" , __FUNCTION__, __LINE__ , lrm_str(rsc->id), retry_interval); rsc->delay_timeout = Gmain_timeout_add(retry_interval , rsc_execution_freeze_timeout, rsc); } break; } if (op->weight && child_count >= max_child_count) { if ((int)rsc->delay_timeout > 0) { lrmd_log(LOG_INFO , "%s:%d: max_child_count (%d) reached and operations on resource %s already delayed" , __FUNCTION__, __LINE__, max_child_count, lrm_str(rsc->id)); } else { lrmd_debug(LOG_NOTICE , "max_child_count (%d) reached, postponing " "execution of %s by %d ms" , max_child_count, op_info(op), retry_interval); rsc->delay_timeout = Gmain_timeout_add(retry_interval , rsc_execution_freeze_timeout, rsc); } break; } if (HA_OK != perform_ra_op(op)) { lrmd_log(LOG_ERR , "unable to perform_ra_op on %s" , op_info(op)); if (HA_OK != ha_msg_add_int(op->msg, F_LRM_OPSTATUS, LRM_OP_ERROR)) { LOG_FAILED_TO_ADD_FIELD("opstatus"); } on_op_done(rsc,op); node = g_list_first(rsc->op_list); } else { break; } } LRMAUDIT(); return HA_OK; } static int store_timestamps(lrmd_op_t* op) { struct ha_msg* msg = op->msg; longclock_t now = time_longclock(), /* tm2unix() needs this */ exec_time = zero_longclock, queue_time = zero_longclock; if (op->t_perform) { queue_time = longclockto_ms(sub_longclock(op->t_perform,op->t_addtolist)); if (op->t_done) { exec_time = longclockto_ms(sub_longclock(op->t_done,op->t_perform)); } } if ((HA_OK!=ha_msg_mod_ul(msg,F_LRM_T_RUN,tm2unix(op->t_perform))) || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_T_RCCHANGE,tm2unix(op->t_rcchange))) || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_EXEC_TIME,exec_time)) || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_QUEUE_TIME,queue_time)) ) { lrmd_log(LOG_ERR,"%s: can not save timestamps to msg",__FUNCTION__); return 1; } return 0; } static void reset_timestamps(lrmd_op_t* op) { op->t_perform = zero_longclock; op->t_done = zero_longclock; cl_msg_remove(op->msg, F_LRM_T_RUN); cl_msg_remove(op->msg, F_LRM_T_RCCHANGE); cl_msg_remove(op->msg, F_LRM_EXEC_TIME); cl_msg_remove(op->msg, F_LRM_QUEUE_TIME); } struct ha_msg* op_to_msg(lrmd_op_t* op) { struct ha_msg* msg = NULL; CHECK_ALLOCATED(op, "op", NULL); if (op->exec_pid == 0) { lrmd_log(LOG_ERR, "%s: op->exec_pid is 0",__FUNCTION__); return NULL; } msg = ha_msg_copy(op->msg); if (NULL == msg) { lrmd_log(LOG_ERR,"%s: can not copy the msg",__FUNCTION__); return NULL; } if ((HA_OK!=ha_msg_mod_int(msg,F_LRM_CALLID,op->call_id))) { lrmd_log(LOG_ERR,"%s: can not save F_LRM_CALLID to msg",__FUNCTION__); ha_msg_del(msg); msg = NULL; } return msg; } /* //////////////////////////////RA wrap funcs/////////////////////////////////// */ int perform_ra_op(lrmd_op_t* op) { int stdout_fd[2]; int stderr_fd[2]; pid_t pid; int timeout; struct RAExecOps * RAExec = NULL; const char* op_type = NULL; GHashTable* params = NULL; GHashTable* op_params = NULL; lrmd_rsc_t* rsc = NULL; ra_pipe_op_t * rapop; LRMAUDIT(); CHECK_ALLOCATED(op, "op", HA_FAIL); rsc = (lrmd_rsc_t*)lookup_rsc(op->rsc_id); CHECK_ALLOCATED(rsc, "rsc", HA_FAIL); if ( pipe(stdout_fd) < 0 ) { cl_perror("%s::%d: pipe", __FUNCTION__, __LINE__); } if ( pipe(stderr_fd) < 0 ) { cl_perror("%s::%d: pipe", __FUNCTION__, __LINE__); } if (op->exec_pid == 0) { lrmd_log(LOG_ERR, "%s::%d: op->exec_pid == 0.", __FUNCTION__, __LINE__); return HA_FAIL; } op_type = ha_msg_value(op->msg, F_LRM_OP); op->t_perform = time_longclock(); check_queue_duration(op); if(HA_OK != ha_msg_value_int(op->msg, F_LRM_TIMEOUT, &timeout)){ timeout = 0; lrmd_log(LOG_ERR,"%s::%d: failed to get timeout for %s" , __FUNCTION__, __LINE__, small_op_info(op)); } if( return_to_orig_privs() ) { cl_perror("%s::%d: failed to raise privileges" , __FUNCTION__, __LINE__); } switch(pid=fork()) { case -1: cl_perror("%s::%d: fork", __FUNCTION__, __LINE__); close(stdout_fd[0]); close(stdout_fd[1]); close(stderr_fd[0]); close(stderr_fd[1]); if( return_to_dropped_privs() ) { cl_perror("%s::%d: failed to drop privileges" , __FUNCTION__, __LINE__); } return HA_FAIL; default: /* Parent */ child_count += op->weight; NewTrackedProc(pid, 1 , debug_level ? ((op->interval && !is_logmsg_due(op)) ? PT_LOGNORMAL : PT_LOGVERBOSE) : PT_LOGNONE , op, &ManagedChildTrackOps); if (!op->interval || is_logmsg_due(op)) { /* log non-repeating ops */ lrmd_log(LOG_INFO,"rsc:%s %s[%d] (pid %d)", rsc->id,probe_str(op,op_type),op->call_id,pid); } else { lrmd_debug(LOG_DEBUG,"rsc:%s %s[%d] (pid %d)", rsc->id,op_type,op->call_id,pid); } close(stdout_fd[1]); close(stderr_fd[1]); rapop = ra_pipe_op_new(stdout_fd[0], stderr_fd[0], op); op->rapop = rapop; op->exec_pid = pid; if (0 < timeout ) { /* Wait 'timeout' ms then send SIGTERM */ /* allow for extra 15 seconds for stonith, * because stonithd handles its children with the * same timeout; in this case the lrmd child * should never timeout, but return the timeout * reported by stonithd */ op->killseq[0].mstimeout = timeout + (!strcmp(rsc->class,"stonith") ? 15000 : 0); op->killseq[0].signalno = SIGTERM; /* Wait 5 seconds then send SIGKILL */ op->killseq[1].mstimeout = 5000; op->killseq[1].signalno = SIGKILL; /* Wait 5 more seconds then moan and complain */ op->killseq[2].mstimeout = 5000; op->killseq[2].signalno = 0; SetTrackedProcTimeouts(pid, op->killseq); } if( return_to_dropped_privs() ) { lrmd_log(LOG_WARNING,"%s::%d: failed to drop privileges: %s" , __FUNCTION__, __LINE__, strerror(errno)); } if ( rapop == NULL) { return HA_FAIL; } LRMAUDIT(); return HA_OK; case 0: /* Child */ #ifdef DEFAULT_REALTIME_POLICY if (sched_getscheduler(0) != SCHED_OTHER) { struct sched_param sp; lrmd_debug(LOG_DEBUG, "perform_ra_op: resetting scheduler class to SCHED_OTHER"); sp.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) cl_perror("%s::%d: sched_setscheduler", __FUNCTION__, __LINE__); } #endif /* Man: The call setpgrp() is equivalent to setpgid(0,0) * _and_ compiles on BSD variants too * need to investigate if it works the same too. */ setpgid(0,0); close(stdout_fd[0]); close(stderr_fd[0]); if (STDOUT_FILENO != stdout_fd[1]) { if (dup2(stdout_fd[1], STDOUT_FILENO)!=STDOUT_FILENO) { cl_perror("%s::%d: dup2" , __FUNCTION__, __LINE__); } close(stdout_fd[1]); } if (STDERR_FILENO != stderr_fd[1]) { if (dup2(stderr_fd[1], STDERR_FILENO)!=STDERR_FILENO) { cl_perror("%s::%d: dup2", __FUNCTION__, __LINE__); } close(stderr_fd[1]); } RAExec = g_hash_table_lookup(RAExecFuncs,rsc->class); if (NULL == RAExec) { close(stdout_fd[1]); close(stderr_fd[1]); lrmd_log(LOG_ERR,"%s::%d: can't find RAExec for class %s" , __FUNCTION__, __LINE__, rsc->class); exit(EXECRA_EXEC_UNKNOWN_ERROR); } /*should we use logging daemon or not in script*/ setenv(HALOGD, cl_log_get_uselogd()?"yes":"no",1); /* Name of the resource and some others also * need to be passed in. Maybe pass through the * entire lrm_op_t too? */ lrmd_debug2(LOG_DEBUG , "perform_ra_op:calling RA plugin to perform %s, pid: [%d]" , op_info(op), getpid()); op_params = ha_msg_value_str_table(op->msg, F_LRM_PARAM); params = merge_str_tables(rsc->params,op_params); if (op_params) { free_str_table(op_params); op_params = NULL; } if (replace_secret_params(rsc->id, params) < 0) { /* replacing secrets failed! */ if (!strcmp(op_type,"stop")) { /* don't fail on stop! */ lrmd_log(LOG_INFO , "%s:%d: proceeding with the stop operation for %s" , __FUNCTION__, __LINE__, rsc->id); } else { lrmd_log(LOG_ERR , "%s:%d: failed to get secrets for %s, " "considering resource not configured" , __FUNCTION__, __LINE__, rsc->id); exit(EXECRA_NOT_CONFIGURED); } } RAExec->execra (rsc->id, rsc->type, rsc->provider, op_type, timeout, params); /* execra should never return. */ exit(EXECRA_EXEC_UNKNOWN_ERROR); } lrmd_log(LOG_ERR, "perform_ra_op: end(impossible)."); return HA_OK; } static void on_ra_proc_registered(ProcTrack* p) { } /* Handle one of our ra child processes finished*/ static void on_ra_proc_finished(ProcTrack* p, int status, int signo, int exitcode , int waslogged) { lrmd_op_t* op = NULL; lrmd_rsc_t* rsc = NULL; struct RAExecOps * RAExec = NULL; const char* op_type; int rc = EXECRA_EXEC_UNKNOWN_ERROR; int ret; int op_status; LRMAUDIT(); CHECK_ALLOCATED(p, "ProcTrack p", ); op = proctrack_data(p); child_count -= op->weight; if (child_count < 0) { lrmd_log(LOG_ERR, "%s:%d: child count is less than zero: %d" , __FUNCTION__, __LINE__, child_count); child_count = 0; } lrmd_debug2(LOG_DEBUG, "on_ra_proc_finished: accessing the op whose " "address is %p", op); CHECK_ALLOCATED(op, "op", ); if (op->exec_pid == 0) { lrmd_log(LOG_ERR, "on_ra_proc_finished: the op was freed."); dump_data_for_debug(); return; } RemoveTrackedProcTimeouts(op->exec_pid); op->exec_pid = -1; rsc = lookup_rsc(op->rsc_id); if (rsc == NULL) { lrmd_log(LOG_ERR, "%s: the rsc (id=%s) does not exist" , __FUNCTION__, lrm_str(op->rsc_id)); lrmd_op_dump(op, __FUNCTION__); lrmd_dump_all_resources(); /* delete the op */ lrmd_op_destroy(op); reset_proctrack_data(p); LRMAUDIT(); return; } RAExec = g_hash_table_lookup(RAExecFuncs,rsc->class); if (NULL == RAExec) { lrmd_log(LOG_ERR,"on_ra_proc_finished: can not find RAExec for" " resource class <%s>", rsc->class); dump_data_for_debug(); return; } op_type = ha_msg_value(op->msg, F_LRM_OP); if ( (NULL == strchr(op->first_line_ra_stdout, '\n')) && (0==STRNCMP_CONST(rsc->class, "heartbeat")) && ( (0==STRNCMP_CONST(op_type, "monitor")) ||(0==STRNCMP_CONST(op_type, "status"))) ) { if ( ( op->rapop != NULL ) && (op->rapop->ra_stdout_fd >= 0) ) { handle_pipe_ra_stdout(op->rapop->ra_stdout_fd , op->rapop); } else { lrmd_log(LOG_WARNING, "There is something wrong: the " "first line isn't read in. Maybe the heartbeat " "does not ouput string correctly for status " "operation. Or the code (myself) is wrong."); } } if( signo ) { if( proctrack_timedout(p) ) { lrmd_log(LOG_WARNING, "%s: pid %d timed out" , small_op_info(op), proctrack_pid(p)); op_status = LRM_OP_TIMEOUT; } else { op_status = LRM_OP_ERROR; } } else { rc = RAExec->map_ra_retvalue(exitcode, op_type , op->first_line_ra_stdout); if (!op->interval || is_logmsg_due(op) || debug_level > 0) { /* log non-repeating ops */ if (rc == exitcode) { lrmd_log(LOG_INFO , "%s: pid %d exited with" " return code %d", small_op_info(op), proctrack_pid(p), rc); }else{ lrmd_log(LOG_INFO , "%s: pid %d exited with" " return code %d (mapped from %d)" , small_op_info(op), proctrack_pid(p), rc, exitcode); } } if (EXECRA_EXEC_UNKNOWN_ERROR == rc || EXECRA_NO_RA == rc) { op_status = LRM_OP_ERROR; lrmd_log(LOG_CRIT , "on_ra_proc_finished: the exit code indicates a problem."); } else { op_status = LRM_OP_DONE; } } if (op->interval && is_logmsg_due(op)) { op->t_lastlogmsg = time_longclock(); } if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_OPSTATUS, op_status)) { LOG_FAILED_TO_ADD_FIELD("opstatus"); return ; } if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_RC, rc)) { LOG_FAILED_TO_ADD_FIELD("F_LRM_RC"); return ; } if ( 0 < strlen(op->first_line_ra_stdout) ) { if (NULL != cl_get_string(op->msg, F_LRM_DATA)) { cl_msg_remove(op->msg, F_LRM_DATA); } ret = ha_msg_add(op->msg, F_LRM_DATA, op->first_line_ra_stdout); if (HA_OK != ret) { LOG_FAILED_TO_ADD_FIELD("data"); } } if (on_op_done(rsc,op) >= 0) { perform_op(rsc); } reset_proctrack_data(p); LRMAUDIT(); } /* Handle the death of one of our managed child processes */ static const char * on_ra_proc_query_name(ProcTrack* p) { static char proc_name[MAX_PROC_NAME]; lrmd_op_t* op = NULL; lrmd_rsc_t* rsc = NULL; const char* op_type = NULL; LRMAUDIT(); op = (lrmd_op_t*)(proctrack_data(p)); if (NULL == op || op->exec_pid == 0) { return "*unknown*"; } op_type = ha_msg_value(op->msg, F_LRM_OP); rsc = lookup_rsc(op->rsc_id); if (rsc == NULL) { snprintf(proc_name , MAX_PROC_NAME , "unknown rsc(%s):%s maybe deleted" , op->rsc_id, op_type); }else { snprintf(proc_name, MAX_PROC_NAME, "%s:%s", rsc->id, op_type); } LRMAUDIT(); return proc_name; } static int get_lrmd_param(const char *name, char *value, int maxstring) { if (!name) { lrmd_log(LOG_ERR, "%s: empty name", __FUNCTION__); return HA_FAIL; } if (!strcmp(name,"max-children")) { snprintf(value, maxstring, "%d", max_child_count); return HA_OK; } else { lrmd_log(LOG_ERR, "%s: unknown lrmd parameter %s", __FUNCTION__, name); return HA_FAIL; } } static int set_lrmd_param(const char *name, const char *value) { int ival; if (!name) { lrmd_log(LOG_ERR, "%s: empty name", __FUNCTION__); return HA_FAIL; } if (!value) { lrmd_log(LOG_ERR, "%s: empty value", __FUNCTION__); return HA_FAIL; } if (!strcmp(name,"max-children")) { ival = atoi(value); if (ival <= 0) { lrmd_log(LOG_ERR, "%s: invalid value for lrmd parameter %s" , __FUNCTION__, name); return HA_FAIL; } else if (ival == max_child_count) { lrmd_log(LOG_INFO, "max-children already set to %d", ival); return HA_OK; } lrmd_log(LOG_INFO, "setting max-children to %d", ival); max_child_count = ival; return HA_OK; } else { lrmd_log(LOG_ERR, "%s: unknown lrmd parameter %s" , __FUNCTION__, name); return HA_FAIL; } } int on_msg_set_lrmd_param(lrmd_client_t* client, struct ha_msg* msg) { const char *name, *value; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); name = ha_msg_value(msg,F_LRM_LRMD_PARAM_NAME); value = ha_msg_value(msg,F_LRM_LRMD_PARAM_VAL); if (!name || !value) { lrmd_log(LOG_ERR, "%s: no parameter defined" , __FUNCTION__); return HA_FAIL; } return set_lrmd_param(name,value); } int on_msg_get_lrmd_param(lrmd_client_t* client, struct ha_msg* msg) { struct ha_msg* ret = NULL; const char *name; char value[MAX_NAME_LEN]; CHECK_ALLOCATED(client, "client", HA_FAIL); CHECK_ALLOCATED(msg, "message", HA_FAIL); ret = create_lrm_ret(HA_OK, 1); CHECK_RETURN_OF_CREATE_LRM_RET; name = ha_msg_value(msg,F_LRM_LRMD_PARAM_NAME); if (get_lrmd_param(name, value, MAX_NAME_LEN) != HA_OK) { return HA_FAIL; } if (HA_OK != ha_msg_add(ret, F_LRM_LRMD_PARAM_VAL, value)) { ha_msg_del(ret); LOG_FAILED_TO_ADD_FIELD(F_LRM_LRMD_PARAM_VAL); return HA_FAIL; } if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) { lrmd_log(LOG_ERR, "%s: can not send the ret msg",__FUNCTION__); } ha_msg_del(ret); return HA_OK; } /* /////////////////Util Functions////////////////////////////////////////////// */ int send_ret_msg (IPC_Channel* ch, int ret) { struct ha_msg* msg = NULL; msg = create_lrm_ret(ret, 1); CHECK_RETURN_OF_CREATE_LRM_RET; if (HA_OK != msg2ipcchan(msg, ch)) { lrmd_log(LOG_ERR, "send_ret_msg: can not send the ret msg"); } ha_msg_del(msg); return HA_OK; } static void send_cbk_msg(struct ha_msg* msg, lrmd_client_t* client) { if (!client) { lrmd_log(LOG_WARNING, "%s: zero client", __FUNCTION__); return; } if (!client->ch_cbk) { lrmd_log(LOG_WARNING, "%s: callback channel is null", __FUNCTION__); } else if (HA_OK != msg2ipcchan(msg, client->ch_cbk)) { lrmd_log(LOG_WARNING, "%s: can not send the ret msg", __FUNCTION__); } } static void send_msg(struct ha_msg* msg, lrmd_client_t* client) { if (!client) { lrmd_log(LOG_WARNING, "%s: zero client", __FUNCTION__); return; } if (HA_OK != ha_msg_mod(msg,F_LRM_APP,client->app_name)) { lrmd_log(LOG_ERR,"%s:%d: cannot add field to a message" , __FUNCTION__, __LINE__); return; } send_cbk_msg(msg, client); } void notify_client(lrmd_op_t* op) { lrmd_client_t* client = lookup_client(op->client_id); if (client) { /* send the result to client */ send_cbk_msg(op->msg, client); } else { lrmd_log(LOG_WARNING , "%s: client for the operation %s does not exist" " and client requested notification." , __FUNCTION__, op_info(op)); } } lrmd_client_t* lookup_client (pid_t pid) { return (lrmd_client_t*) g_hash_table_lookup(clients, &pid); } static gboolean client_cmp_name(gpointer key, gpointer val, gpointer app_name) { return strcmp(((lrmd_client_t*)val)->app_name,(char *)app_name) ? FALSE : TRUE; } static lrmd_client_t* lookup_client_by_name(char *app_name) { return (lrmd_client_t*)g_hash_table_find(clients,client_cmp_name,app_name); } lrmd_rsc_t* lookup_rsc (const char* rid) { return rid ? (lrmd_rsc_t*)g_hash_table_lookup(resources, rid) : NULL; } lrmd_rsc_t* lookup_rsc_by_msg (struct ha_msg* msg) { const char* id = NULL; lrmd_rsc_t* rsc = NULL; CHECK_ALLOCATED(msg, "msg", NULL); id = ha_msg_value(msg, F_LRM_RID); if (id == NULL) { lrmd_log(LOG_ERR, "lookup_rsc_by_msg: got a NULL resource id."); return NULL; } if (RID_LEN <= strnlen(id, RID_LEN+2)) { lrmd_log(LOG_ERR, "lookup_rsc_by_msg: resource id is too long."); return NULL; } rsc = lookup_rsc(id); return rsc; } static void destroy_pipe_ra_stdout(gpointer user_data) { ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data; CHECK_ALLOCATED(rapop, "ra_pipe_op",); if (rapop->ra_stderr_fd < 0) { ra_pipe_op_destroy(rapop); } } static void destroy_pipe_ra_stderr(gpointer user_data) { ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data; CHECK_ALLOCATED(rapop, "ra_pipe_op",); if (rapop->ra_stdout_fd < 0) { ra_pipe_op_destroy(rapop); } } static gboolean handle_pipe_ra_stdout(int fd, gpointer user_data) { gboolean rc = TRUE; ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data; char * data = NULL; lrmd_op_t* lrmd_op = NULL; CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE); if (rapop->lrmd_op == NULL) { lrmd_debug2(LOG_DEBUG, "%s:%d: Unallocated lrmd_op 0x%lx!!" , __FUNCTION__, __LINE__ , (unsigned long)rapop->lrmd_op); } else { lrmd_op = rapop->lrmd_op; } if (fd <= STDERR_FILENO) { lrmd_log(LOG_CRIT, "%s:%d: Attempt to read from " "closed/invalid file descriptor %d." , __FUNCTION__, __LINE__, fd); return FALSE; } if (0 != read_pipe(fd, &data, rapop)) { /* error or reach the EOF */ if (fd > STDERR_FILENO) { close(fd); if (fd == rapop->ra_stdout_fd) { rapop->ra_stdout_fd = -1; } } if ( NULL != rapop->ra_stdout_gsource) { /* * Returning FALSE will trigger ipc code to release * the GFDSource, so donn't release it here. */ rapop->ra_stdout_gsource = NULL; } rc = FALSE; } if ( data!=NULL ) { if ( (0==STRNCMP_CONST(rapop->op_type, "meta-data")) ||(0==STRNCMP_CONST(rapop->op_type, "monitor")) ||(0==STRNCMP_CONST(rapop->op_type, "status")) ) { lrmd_debug(LOG_DEBUG, "RA output: (%s:%s:stdout) %s" , lrm_str(rapop->rsc_id), rapop->op_type, data); } else { lrmd_log(LOG_INFO, "RA output: (%s:%s:stdout) %s" , lrm_str(rapop->rsc_id), rapop->op_type, data); } /* * This code isn't good enough, it produces erratic and hard-to * read messages in the logs. But this does not affect the * function correctness, since the first line output is ensured * to be collected into the buffer completely. * Anyway, the meta-data (which is _many_ lines long) can be * handled by another function, see raexec.h */ if ( (rapop->first_line_read == FALSE) && (0==STRNCMP_CONST(rapop->rsc_class, "heartbeat")) && ( lrmd_op != NULL ) && ( (0==STRNCMP_CONST(rapop->op_type, "monitor")) ||(0==STRNCMP_CONST(rapop->op_type, "status")) )) { if (lrmd_op != NULL) { strncat(lrmd_op->first_line_ra_stdout, data , sizeof(lrmd_op->first_line_ra_stdout) - strlen(lrmd_op->first_line_ra_stdout)-1); if (strchr(lrmd_op->first_line_ra_stdout, '\n') != NULL) { rapop->first_line_read = TRUE; } } else { lrmd_log(LOG_CRIT , "Before read the first line, the RA " "execution child quitted and waited."); } } g_free(data); } return rc; } static gboolean handle_pipe_ra_stderr(int fd, gpointer user_data) { gboolean rc = TRUE; char * data = NULL; ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data; CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE); if (fd <= STDERR_FILENO) { lrmd_log(LOG_CRIT, "%s:%d: Attempt to read from " " closed/invalid file descriptor %d." , __FUNCTION__, __LINE__, fd); return FALSE; } if (0 != read_pipe(fd, &data, rapop)) { /* error or reach the EOF */ if (fd > STDERR_FILENO) { close(fd); if (fd == rapop->ra_stderr_fd) { rapop->ra_stderr_fd = -1; } } if ( NULL != rapop->ra_stderr_gsource) { /* * G_main_del_fd will trigger * destroy_pipe_ra_stderr * ra_pipe_op_destroy * * Returning FALSE will trigger ipc code to release * the GFDSource, so donn't release it here. */ rapop->ra_stderr_gsource = NULL; } rc = FALSE; } if (data!=NULL) { lrmd_log(LOG_INFO, "RA output: (%s:%s:stderr) %s" , lrm_str(rapop->rsc_id), probe_str(rapop->lrmd_op,rapop->op_type), data); g_free(data); } return rc; } int read_pipe(int fd, char ** data, void * user_data) { const int BUFFLEN = 81; char buffer[BUFFLEN]; int readlen; GString * gstr_tmp; int rc = 0; lrmd_op_t * op = NULL; ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data; lrmd_debug3(LOG_DEBUG, "%s begin.", __FUNCTION__); CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE); op = (lrmd_op_t *)rapop->lrmd_op; if (NULL == op) { lrmd_debug2(LOG_DEBUG, "%s:%d: Unallocated lrmd_op 0x%lx!!" , __FUNCTION__, __LINE__ , (unsigned long)op); } *data = NULL; gstr_tmp = g_string_new(""); do { errno = 0; readlen = read(fd, buffer, BUFFLEN - 1); if (NULL == op) { lrmd_debug2(LOG_NOTICE , "read's ret: %d when lrmd_op finished" , readlen); } if ( readlen > 0 ) { buffer[readlen] = EOS; g_string_append(gstr_tmp, buffer); } } while (readlen == BUFFLEN - 1 || errno == EINTR); if (errno == EINTR || errno == EAGAIN) { errno = 0; } /* Reach the EOF */ if (readlen == 0) { rc = -1; } if ((readlen < 0) && (errno !=0)) { rc = -1; switch (errno) { default: cl_perror("%s:%d read error: fd %d errno=%d" , __FUNCTION__, __LINE__ , fd, errno); if (NULL != op) { lrmd_op_dump(op, "op w/bad errno"); } else { lrmd_log(LOG_NOTICE , "%s::%d: lrmd_op has been freed" , __FUNCTION__, __LINE__); } break; case EBADF: lrmd_log(LOG_CRIT , "%s:%d" " Attempt to read from closed file descriptor %d." , __FUNCTION__, __LINE__, fd); if (NULL != op) { lrmd_op_dump(op, "op w/bad errno"); } else { lrmd_log(LOG_NOTICE , "%s::%d: lrmd_op has been freed" , __FUNCTION__, __LINE__); } break; } } if ( gstr_tmp->len == 0 ) { g_string_free(gstr_tmp, TRUE); } else { *data = gstr_tmp->str; g_string_free(gstr_tmp, FALSE); } lrmd_debug3(LOG_DEBUG, "%s end.", __FUNCTION__); return rc; } static gboolean debug_level_adjust(int nsig, gpointer user_data) { char s[16]; switch (nsig) { case SIGUSR1: debug_level++; dump_data_for_debug(); break; case SIGUSR2: dump_data_for_debug(); debug_level--; if (debug_level < 0) { debug_level = 0; } break; default: lrmd_log(LOG_WARNING, "debug_level_adjust: Received an " "unexpected signal(%d). Something wrong?.",nsig); } snprintf(s, sizeof(s), "%d", debug_level); setenv(HADEBUGVAL, s, 1); return TRUE; } static void dump_data_for_debug(void) { lrmd_debug(LOG_DEBUG, "begin to dump internal data for debugging."); lrmd_dump_all_clients(); lrmd_dump_all_resources(); lrmd_debug(LOG_DEBUG, "end to dump internal data for debugging."); } const char* gen_op_info(const lrmd_op_t* op, gboolean add_params) { static char info[512]; lrmd_rsc_t* rsc = NULL; const char * op_type; GString * param_gstr; GHashTable* op_params = NULL; if (NULL == op) { lrmd_log(LOG_ERR, "%s:%d: op==NULL" , __FUNCTION__, __LINE__); return NULL; } rsc = lookup_rsc(op->rsc_id); op_type = ha_msg_value(op->msg, F_LRM_OP); if (rsc == NULL) { snprintf(info,sizeof(info) ,"operation %s[%d] on unknown rsc(maybe deleted) for client %d" ,lrm_str(op_type) ,op->call_id ,op->client_id); }else{ if (op->exec_pid > 1) { snprintf(info, sizeof(info) ,"operation %s[%d] with pid %d on %s for client %d" ,lrm_str(op_type), op->call_id, op->exec_pid, lrm_str(rsc->id) ,op->client_id); } else { snprintf(info, sizeof(info) ,"operation %s[%d] on %s for client %d" ,lrm_str(op_type), op->call_id, lrm_str(rsc->id) ,op->client_id); } if( add_params ) { param_gstr = g_string_new(""); op_params = ha_msg_value_str_table(op->msg, F_LRM_PARAM); hash_to_str(op_params, param_gstr); if (op_params) { free_str_table(op_params); op_params = NULL; } snprintf(info+strlen(info), sizeof(info)-strlen(info) ,", its parameters: %s",param_gstr->str); g_string_free(param_gstr, TRUE); } } return info; } static void hash_to_str(GHashTable * params , GString * str) { if (params) { g_hash_table_foreach(params, hash_to_str_foreach, str); } } static void hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data) { char buffer_tmp[80]; GString * str = (GString *)user_data; g_snprintf(buffer_tmp, sizeof(buffer_tmp), "%s=[%s] " , (char *)key, (char *)value); str = g_string_append(str, buffer_tmp); } static void check_queue_duration(lrmd_op_t* op) { unsigned long t_stay_in_list = 0; static struct msg_ctrl *ml; CHECK_ALLOCATED(op, "op", ); t_stay_in_list = longclockto_ms(op->t_perform - op->t_addtolist); if ( t_stay_in_list > WARNINGTIME_IN_LIST) { if (!ml) ml = cl_limit_log_new(logmsg_ctrl_defs + OP_STAYED_TOO_LONG); cl_limit_log(ml, LOG_WARNING , "perform_ra_op: the %s stayed in operation " "list for %lu ms (longer than %d ms)" , small_op_info(op), t_stay_in_list , WARNINGTIME_IN_LIST ); if (debug_level >= 2) { dump_data_for_debug(); } } } Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/lrmd.h0000644000000000000000000001774412120057602024135 0ustar00usergroup00000000000000#define MAX_PID_LEN 256 #define MAX_PROC_NAME 256 #define MAX_MSGTYPELEN 32 #define MAX_CLASSNAMELEN 32 #define WARNINGTIME_IN_LIST 10000 #define OPTARGS "skrhvmi:" #define PID_FILE HA_VARRUNDIR"/lrmd.pid" #define LRMD_COREDUMP_ROOT_DIR HA_COREDIR #define APPHB_WARNTIME_FACTOR 3 #define APPHB_INTVL_DETLA 30 /* Millisecond */ #define lrmd_log(priority, fmt...); \ cl_log(priority, fmt); #define lrmd_debug(priority, fmt...); \ if ( debug_level >= 1 ) { \ cl_log(priority, fmt); \ } #define lrmd_debug2(priority, fmt...); \ if ( debug_level >= 2 ) { \ cl_log(priority, fmt); \ } #define lrmd_debug3(priority, fmt...); \ if ( debug_level >= 3 ) { \ cl_log(priority, fmt); \ } #define lrmd_nullcheck(p) ((p) ? (p) : "") #define lrm_str(p) (lrmd_nullcheck(p)) #define CHECK_ALLOCATED(thing, name, result) \ if (!thing) { \ lrmd_log(LOG_ERR \ , "%s: %s pointer 0x%lx is not allocated." \ , __FUNCTION__, name, (unsigned long)thing); \ if (!in_alloc_dump) { \ in_alloc_dump = TRUE; \ dump_data_for_debug(); \ in_alloc_dump = FALSE; \ return result; \ } \ } #define CHECK_RETURN_OF_CREATE_LRM_RET do { \ if (NULL == msg) { \ lrmd_log(LOG_ERR \ , "%s: cannot create a ret message with create_lrm_ret." \ , __FUNCTION__); \ return HA_FAIL; \ } \ } while(0) #define LOG_FAILED_TO_GET_FIELD(field) \ lrmd_log(LOG_ERR \ , "%s:%d: cannot get field %s from message." \ ,__FUNCTION__,__LINE__,field) #define LOG_FAILED_TO_ADD_FIELD(field) \ lrmd_log(LOG_ERR \ , "%s:%d: cannot add the field %s to a message." \ , __FUNCTION__ \ , __LINE__ \ , field) /* NB: There's a return in these macros, hence the names */ #define return_on_no_int_value(msg,fld,i) do { \ if (HA_OK != ha_msg_value_int(msg,fld,i)) { \ LOG_FAILED_TO_GET_FIELD(fld); \ return HA_FAIL; \ } \ } while(0) #define return_on_no_value(msg,fld,v) do { \ v = ha_msg_value(msg,fld); \ if (!v) { \ LOG_FAILED_TO_GET_FIELD(fld); \ return HA_FAIL; \ } \ } while(0) #define LRMD_APPHB_HB \ if (reg_to_apphb == TRUE) { \ if (apphb_hb() != 0) { \ reg_to_apphb = FALSE; \ } \ } #define tm2age(tm) \ (cmp_longclock(tm, zero_longclock) <= 0) ? \ 0 : longclockto_ms(sub_longclock(now, tm)) #define tm2unix(tm) \ (time(NULL)-(tm2age(tm)+999)/1000) /* * The basic objects in our world: * * lrmd_client_t: * Client - a process which has connected to us for service. * * lrmd_rsc_t: * Resource - an abstract HA cluster resource implemented by a * resource agent through our RA plugins * It has two list of operations (lrmd_op_t) associated with it * op_list - operations to be run as soon as they're ready * repeat_op_list - operations to be run later * It maintains the following tracking structures: * last_op_done Last operation performed on this resource * last_op_table Last operations of each type done per client * * lrmd_op_t: * Resource operation - an operation on a resource -- requested * by a client. * * ProcTrack - tracks a currently running resource operation. * It points back to the lrmd_op_t that started it. * * Global structures containing these things: * * clients - a hash table of all (currently connected) clients * * resources - a hash table of all (currently configured) resources * * Proctrack keeps its own private data structures to keep track of * child processes that it created. They in turn point to the * lrmd_op_t objects that caused us to fork the child process. * * */ /* * Recognized privilege levels */ #define PRIV_ADMIN 8 /* ADMIN_UIDS are administrators */ #define ADMIN_UIDS "0,"HA_CCMUSER #define ADMIN_GIDS "0,"HA_APIGROUP /* unused */ typedef struct { char* app_name; pid_t pid; gid_t gid; uid_t uid; IPC_Channel* ch_cmd; IPC_Channel* ch_cbk; GCHSource* g_src; GCHSource* g_src_cbk; char lastrequest[MAX_MSGTYPELEN]; time_t lastreqstart; time_t lastreqend; time_t lastrcsent; int priv_lvl; /* client privilege level (depends on uid/gid) */ }lrmd_client_t; typedef struct lrmd_rsc lrmd_rsc_t; typedef struct lrmd_op lrmd_op_t; typedef struct ra_pipe_op ra_pipe_op_t; #define RSC_REMOVAL_PENDING 1 #define RSC_FLUSHING_OPS 2 #define rsc_frozen(r) \ ((r)->state==RSC_REMOVAL_PENDING || (r)->state==RSC_FLUSHING_OPS) #define rsc_removal_pending(r) \ ((r)->state==RSC_REMOVAL_PENDING) #define set_rsc_removal_pending(r) \ (r)->state = RSC_REMOVAL_PENDING #define set_rsc_flushing_ops(r) \ (r)->state = RSC_FLUSHING_OPS #define rsc_reset_state(r) (r)->state = 0 /* log messages for repeating ops (monitor) once an hour */ #define LOGMSG_INTERVAL (60*60) #define is_logmsg_due(op) \ (longclockto_ms(sub_longclock(time_longclock(), op->t_lastlogmsg))/1000 >= \ (unsigned long)LOGMSG_INTERVAL) #define probe_str(op,op_type) \ ((op && !op->interval && !strcmp(op_type,"monitor")) ? "probe" : op_type) /* exclude stonith class from child count */ #define no_child_count(rsc) \ (strcmp((rsc)->class,"stonith") == 0) struct lrmd_rsc { char* id; /* Unique resource identifier */ char* type; /* */ char* class; /* */ char* provider; /* Resource provider (optional) */ GHashTable* params; /* Parameters to this resource */ /* as name/value pairs */ GList* op_list; /* Queue of operations to run */ GList* repeat_op_list; /* Unordered list of repeating */ /* ops They will run later */ GHashTable* last_op_table; /* Last operation of each type */ lrmd_op_t* last_op_done; /* The last finished op of the resource */ guint delay_timeout; /* The delay value of op_list execution */ int state; /* status of the resource */ }; struct lrmd_op { char* rsc_id; gboolean is_copy; pid_t client_id; int call_id; int exec_pid; guint repeat_timeout_tag; int interval; int delay; gboolean is_cancelled; int weight; int copyparams; struct ha_msg* msg; ra_pipe_op_t * rapop; char first_line_ra_stdout[80]; /* only for heartbeat RAs*/ /*time stamps*/ longclock_t t_recv; /* set in lrmd_op_new(), i.e. on op create */ longclock_t t_addtolist; /* set in add_op_to_runlist() */ longclock_t t_perform; /* set in perform_ra_op() */ longclock_t t_done; /* set in on_op_done() */ longclock_t t_rcchange; /* set in on_op_done(), could equal t_perform */ longclock_t t_lastlogmsg; /* the last time the monitor op was logged */ ProcTrackKillInfo killseq[3]; }; /* For reading the output from executing the RA */ struct ra_pipe_op { /* The same value of the one in corresponding lrmd_op */ lrmd_op_t * lrmd_op; int ra_stdout_fd; int ra_stderr_fd; GFDSource * ra_stdout_gsource; GFDSource * ra_stderr_gsource; gboolean first_line_read; /* For providing more detailed information in log */ char * rsc_id; char * op_type; char * rsc_class; }; const char *gen_op_info(const lrmd_op_t* op, gboolean add_params); #define op_info(op) gen_op_info(op,TRUE) #define small_op_info(op) gen_op_info(op,FALSE) #define DOLRMAUDITS #undef DOLRMAUDITS #define DOMEGALRMAUDITS #define LRMAUDIT_CLIENTS #define LRMAUDIT_RESOURCES #ifdef DOLRMAUDITS void lrmd_audit(const char *function, int line); void audit_clients(void); void audit_resources(void); void audit_ops(GList* rsc_ops, lrmd_rsc_t *rsc, const char *desc); void on_client(gpointer key, gpointer value, gpointer user_data); void on_resource(gpointer key, gpointer value, gpointer user_data); void on_op(lrmd_op_t *op, lrmd_rsc_t *rsc, const char *desc); void on_ra_pipe_op(ra_pipe_op_t *rapop, lrmd_op_t *op, const char *desc); # define LRMAUDIT() lrmd_audit(__FUNCTION__,__LINE__) # ifdef DOMEGALRMAUDITS # define MEGALRMAUDIT lrmd_audit(__FUNCTION__,__LINE__) # else # define MEGALRMAUDIT /*nothing*/ # endif #else # define LRMAUDIT() /*nothing*/ # define MEGALRMAUDIT() /*nothing*/ #endif /* * load parameters from an ini file (cib_secrets.c) */ int replace_secret_params(char* rsc_id, GHashTable* params); Reusable-Cluster-Components-glue--3cff550e1084/lrm/lrmd/lrmd_fdecl.h0000644000000000000000000001252712120057602025264 0ustar00usergroup00000000000000/* TODO: This ought to be broken up into several source files for easier * reading and debugging. */ /* Debug oriented funtions */ static gboolean debug_level_adjust(int nsig, gpointer user_data); static void dump_data_for_debug(void); /* glib loop call back functions */ static gboolean on_connect_cmd(IPC_Channel* ch_cmd, gpointer user_data); static gboolean on_connect_cbk(IPC_Channel* ch_cbk, gpointer user_data); static int msg_type_cmp(const void *p1, const void *p2); static gboolean on_receive_cmd(IPC_Channel* ch_cmd, gpointer user_data); static gboolean on_repeat_op_readytorun(gpointer data); static void on_remove_client(gpointer user_data); static void destroy_pipe_ra_stderr(gpointer user_data); static void destroy_pipe_ra_stdout(gpointer user_data); /* message handlers */ static int on_msg_register(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_rsc_classes(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_rsc_types(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_rsc_providers(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_metadata(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_add_rsc(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_rsc(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_last_op(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_all(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_del_rsc(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_fail_rsc(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_cancel_op(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_flush_all(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_perform_op(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_state(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_set_lrmd_param(lrmd_client_t* client, struct ha_msg* msg); static int on_msg_get_lrmd_param(lrmd_client_t* client, struct ha_msg* msg); static int set_lrmd_param(const char *name, const char *value); static int get_lrmd_param(const char *name, char *value, int maxstring); static gboolean sigterm_action(int nsig, gpointer unused); /* functions wrap the call to ra plugins */ static int perform_ra_op(lrmd_op_t* op); /* Apphb related functions */ static int init_using_apphb(void); static gboolean emit_apphb(gpointer data); /* Utility functions */ static int flush_op(lrmd_op_t* op); static gboolean rsc_execution_freeze_timeout(gpointer data); static void add_op_to_runlist(lrmd_rsc_t* rsc, lrmd_op_t* op); static int perform_op(lrmd_rsc_t* rsc); static int unregister_client(lrmd_client_t* client); static int on_op_done(lrmd_rsc_t* rsc, lrmd_op_t* op); static int send_ret_msg ( IPC_Channel* ch, int rc); static void send_cbk_msg(struct ha_msg* msg, lrmd_client_t* client); static void send_msg(struct ha_msg* msg, lrmd_client_t* client); static void notify_client(lrmd_op_t* op); static lrmd_client_t* lookup_client (pid_t pid); static lrmd_rsc_t* lookup_rsc (const char* rid); static lrmd_rsc_t* lookup_rsc_by_msg (struct ha_msg* msg); static int read_pipe(int fd, char ** data, gpointer user_data); static gboolean handle_pipe_ra_stdout(int fd, gpointer user_data); static gboolean handle_pipe_ra_stderr(int fd, gpointer user_data); static struct ha_msg* op_to_msg(lrmd_op_t* op); static int store_timestamps(lrmd_op_t* op); static void reset_timestamps(lrmd_op_t* op); static gboolean lrm_shutdown(void); static gboolean can_shutdown(void); static gboolean free_str_hash_pair(gpointer key , gpointer value, gpointer user_data); static gboolean free_str_op_pair(gpointer key , gpointer value, gpointer user_data); static lrmd_op_t* lrmd_op_copy(const lrmd_op_t* op); static void send_last_op(gpointer key, gpointer value, gpointer user_data); static void replace_last_op(lrmd_client_t* client, lrmd_rsc_t* rsc, lrmd_op_t* op); static int record_op_completion(lrmd_rsc_t* rsc, lrmd_op_t* op); static void to_repeatlist(lrmd_rsc_t* rsc, lrmd_op_t* op); static void remove_op_history(lrmd_op_t* op); static void hash_to_str(GHashTable * , GString *); static void hash_to_str_foreach(gpointer key, gpointer value, gpointer userdata); static void warning_on_active_rsc(gpointer key, gpointer value, gpointer user_data); static void check_queue_duration(lrmd_op_t* op); static gboolean flush_all(GList** listp, int client_pid); static gboolean cancel_op(GList** listp,int cancel_op_id); static int prepare_failmsg(struct ha_msg* msg, int fail_rc, const char *fail_reason); static void async_notify(gpointer key, gpointer val, gpointer data); static gboolean client_cmp_name(gpointer key, gpointer val, gpointer app_name); static lrmd_client_t* lookup_client_by_name(char *app_name); static void calc_max_children(void); /* * following functions are used to monitor the exit of ra proc */ static void on_ra_proc_registered(ProcTrack* p); static void on_ra_proc_finished(ProcTrack* p, int status , int signo, int exitcode, int waslogged); static const char* on_ra_proc_query_name(ProcTrack* p); /* * Daemon functions * * copy from the code of Andrew Beekhof */ static void usage(const char* cmd, int exit_status); static int init_start(void); static int init_stop(const char *pid_file); static int init_status(const char *pid_file, const char *client_name); static void lrmd_rsc_dump(char* rsc_id, const char * text); Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/LRMBasicSanityCheck.in0000755000000000000000000000256412120057602027116 0ustar00usergroup00000000000000#!/bin/sh # Copyright (c) 2004 International Business Machines # Author: Huang Zhen # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # HBLIB=@libdir@/heartbeat LRMD=$HBLIB/lrmd LRMADMIN=@sbindir@/lrmadmin export LRMD LRMADMIN if [ $# -gt 0 ]; then LRMD=$1/lrmd fi if [ ! -f $LRMD ]; then echo $LRMD does not exist exit 1 fi if [ ! -f $LRMADMIN ]; then echo $LRMADMIN does not exist exit 1 fi OUTDIR=/tmp/LRM_BSC_$$ export OUTDIR [ -d $OUTDIR ] && { echo $OUTDIR exists, please cleanup exit 1 } `dirname $0`/regression.sh -q set:BSC rc=$? if [ $rc -eq 0 ]; then echo "LRM tests PASSED" rm -rf $OUTDIR else echo "LRM tests FAILED" echo "Please check $OUTDIR for results" fi exit $rc Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/Makefile.am0000644000000000000000000000371012120057602025067 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = testcases INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la $(GLIBLIB) noinst_PROGRAMS = apitest plugintest callbacktest apitest_SOURCES = apitest.c apitest_LDFLAGS = $(COMMONLIBS) apitest_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la apitest_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la plugintest_SOURCES = plugintest.c plugintest_LDADD = $(COMMONLIBS) plugintest_LDFLAGS = -L$(top_builddir)/lib/pils -lpils @LIBLTDL@ callbacktest_SOURCES = callbacktest.c callbacktest_LDFLAGS = $(COMMONLIBS) callbacktest_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la callbacktest_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la testdir = $(datadir)/$(PACKAGE_NAME)/lrmtest test_SCRIPTS = LRMBasicSanityCheck regression.sh evaltest.sh lrmregtest lrmregtest-lsb test_DATA = README.regression defaults descriptions lrmadmin-interface language # shouldn't need this, but we do for some versions of autofoo tools EXTRA_DIST = $(test_SCRIPTS) $(test_DATA) Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/README.regression0000644000000000000000000001133412120057602026073 0ustar00usergroup00000000000000LRM regression tests * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * * * evaltest.sh uses eval to an extent you don't really want to * know about. Beware. Beware twice. Any input from the testcases * directory is considered to be trusted. So, think twice before * devising your tests lest you kill your precious data. Got it? * Good. * * Furthermore, we are deliberately small on testing the user * input and no one should try to predict what is to happen on * random input from the testcases. * * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * Manifest regression.sh: the top level program evaltest.sh: the engine test engine lrmadmin-interface: interface to lrmd (lrmadmin) descriptions: describe what we are about to do defaults: the default settings for test commands testcases/: here are the testcases and filters output/: here goes the output All volatile data lives in the testcases/ directory. NB: You should never ever need to edit regression.sh and evaltest.sh. If you really have to, please talk to me and I will try to fix it so that you do not have to. Please write new test cases. The more the merrier :) Usage The usage is: ./regression.sh ["prepare"] ["set:"|] Test cases are collected in test sets. The default test set is basicset and running regression.sh without arguments will do all tests from that set. To show progress, for each test a '.' is printed. For sleeps, a '+' for each second. Once all tests have been evaluated, the output is checked against the expect file. If successful, "PASS" is printed, otherwise "FAIL". Specifying "prepare" will make regression.sh create expect output files for the given set of tests or testcase. The script will start and stop lrmd itself. stonithd is also started to test the XML descriptions printed by stonith agents. No other parts of stonithd functionality is tested. The following files may be generated: output/.out: the output of the testcase output/regression.out: the output of regression.sh output/lrmd.out: the output of lrmd On success output from testcases is removed and regression.out is empty. Driving the test cases yourself evaltest.sh accepts input from stdin, evaluates it immediately, and prints results to stdout/stderr. One can perhaps get a better feeling of what's actually going on by running it interactively. Please note that you have to start the lrmd yourself beforehand. Test cases Tests are written in a simple metalanguage. The list of commands with rough translation to lrmadmin's options is in the language file. The best description of the language is in the lrmadmin-interface and descriptions scripts: $ egrep '^lrm|echo' lrmadmin-interface descriptions A test case is a list of tests, one per line. A few examples: add # add a resource with default name list # list all resources del rsc=wiwi # remove a wiwi resource A set of defaults for LRM options is in the defaults file. That's why we can write short forms instead of add rsc=r1 class=ocf type=lrmregtest provider=heartbeat ... Special operations There are special operations with which it is possible to change environment and do other useful things. All special ops start with the '%' sign and may be followed by additional parameters. %setenv change the environment variable; see defaults for the set of global variables and resetvars() in evaltest.sh %sleep sleep %stop skip the rest of the tests %extcheck feed the output of the next test case to the specified external program/filter; the program should either reside in testcases/ or be in the PATH, i.e. %extcheck cat simulates a null op :) see testcases/metadata for some examples %repeat num repeat the next test num times there are several variables which are substituted in the test lines, so that we can simulate a for loop: s/%t/$test_cnt/g s/%l/$line/g s/%j/$job_cnt/g s/%i/$repeat_cnt/g for example, to add 10 resources: %repeat 10 add rsc=r-%i %bg [num] run next num (or just the next one) tests in background %bgrepeat [num] a combination of the previous two (used often) %wait wait for the last background test to finish %shell feed whatever is in the rest of the line to 'sh -s' Filters and except files Some output is necessarily very volatile, such as time stamps. It is possible to specify a filter for each testcase to get rid of superfluous information. A filter is a filter in UNIX sense, it takes input from stdin and prints results to stdout. There is a common filter called very inventively testcases/common.filter which is applied to all test cases. Except files are a list of extended regular expressions fed to egrep(1). That way one can filter out lines which are not interesting. Again, the one applied to all is testcases/common.excl. Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/apitest.c0000644000000000000000000002025412120057602024652 0ustar00usergroup00000000000000 /* * Test program for Local Resource Manager API. * * Copyright (C) 2004 Huang Zhen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include void lrm_op_done_callback (lrm_op_t* op); void printf_rsc(lrm_rsc_t* rsc); void printf_op(lrm_op_t* op); void printf_hash_table(GHashTable* hash_table); void get_all_rsc(ll_lrm_t* lrm); void get_cur_state(lrm_rsc_t* rsc); int main (int argc, char* argv[]) { ll_lrm_t* lrm; lrm_rsc_t* rsc = NULL; lrm_op_t* op = NULL; const char* rid = "ip248"; GHashTable* param = NULL; GList* classes; int i; cl_log_set_entity("apitest"); cl_log_set_facility(LOG_USER); lrm = ll_lrm_new("lrm"); if(NULL == lrm) { printf("lrm==NULL\n"); return 1; } puts("sigon..."); lrm->lrm_ops->signon(lrm,"apitest"); classes = lrm->lrm_ops->get_rsc_class_supported(lrm); lrm_free_str_list(classes); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); puts("add_rsc..."); lrm->lrm_ops->add_rsc(lrm, rid, "heartbeat", "IPaddr", "heartbeat", param); puts("get_rsc..."); rsc = lrm->lrm_ops->get_rsc(lrm, rid); printf_rsc(rsc); puts("perform_op(start)..."); op = lrm_op_new(); op->op_type = g_strdup("start"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a start op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc = EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(status)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("status"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a status op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 1000; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(stop)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("stop"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a stop op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(status)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("status"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a status op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 2000; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(start)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("start"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a start op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc = EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(status)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("status"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a status op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 3000; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); puts("perform_op(stop)..."); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100")); op = lrm_op_new(); op->op_type = g_strdup("stop"); op->params = param; op->timeout = 0; op->user_data = strdup("It is a stop op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); lrm_free_op(op); for(i = 0; i < 5; i++) { puts("get_cur_state..."); get_cur_state(rsc); puts("sleep a while..."); sleep(1); } puts("delete_rsc..."); lrm->lrm_ops->delete_rsc(lrm, rid); lrm_free_rsc(rsc); puts("signoff..."); lrm->lrm_ops->signoff(lrm); return 0; } void lrm_op_done_callback(lrm_op_t* op) { puts("lrm_op_done_callback..."); printf_op(op); } void printf_rsc(lrm_rsc_t* rsc) { printf("print resource>>>>>>>>>\n"); if (NULL == rsc) { printf("resource is null\n"); printf("print end\n"); return; } printf("\tresource of id:%s\n", rsc->id); printf("\ttype:%s\n", rsc->type); printf("\tclass:%s\n", rsc->class); printf("\tparams:\n"); printf_hash_table(rsc->params); printf("print end<<<<<<<<<<<<<<<\n"); } void printf_op(lrm_op_t* op) { printf("print op>>>>>>>>>>>>>>>>\n"); if (NULL == op) { printf("op is null\n"); printf("print end\n"); return; } printf("\top_type:%s\n",op->op_type?op->op_type:"null"); printf("\tparams:\n"); printf_hash_table(op->params); printf("\ttimeout:%d\n",op->timeout); printf("\tuser_data:%s\n",op->user_data?(char*)op->user_data:"null"); printf("\top_status:%d\n",op->op_status); printf("\tapp_name:%s\n",op->app_name?op->app_name:"null"); printf("\toutput:%s\n",op->output?op->output:"null"); printf("\trc:%d\n",op->rc); printf("\tcall_id:%d\n",op->call_id); printf("print end<<<<<<<<<<<<<<<<<<\n"); } static void printf_pair(gpointer key, gpointer value, gpointer user_data) { printf("\t\t%s=%s\n",(char*)key,(char*)value); } void printf_hash_table(GHashTable* hash_table) { if (NULL == hash_table) { printf("\t\tnull\n"); return; } g_hash_table_foreach(hash_table, printf_pair, NULL); } void get_all_rsc(ll_lrm_t* lrm) { GList* element = NULL, * rid_list = NULL; puts("get_all_rscs..."); rid_list = lrm->lrm_ops->get_all_rscs(lrm); if (NULL != rid_list) { element = g_list_first(rid_list); while (NULL != element) { printf("\tid:%s\n",(char*)element->data); element = g_list_next(element); } } else { puts("\tnone."); } lrm_free_str_list(rid_list); } void get_cur_state(lrm_rsc_t* rsc) { state_flag_t state; GList* node = NULL, * op_list = NULL; lrm_op_t* op = NULL; printf("current state>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); op_list = rsc->ops->get_cur_state(rsc, &state); printf("\tcurrent state:%s\n",state==LRM_RSC_IDLE?"Idle":"Busy"); for(node = g_list_first(op_list); NULL != node; node = g_list_next(node)) { op = (lrm_op_t*)node->data; printf_op(op); } lrm_free_op_list(op_list); printf("current end<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/apitest.exp0000644000000000000000000000331212120057602025220 0ustar00usergroup00000000000000sigon... add_rsc... get_rsc... print resource resource of id:ip248 type:IPv6addr class:heartbeat params: 1=3ffe:ffff:0:f101::3 print end perform_op(start)... print op op_type:start params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a start op! op_status:0 app_name:null output:null rc:0 print end perform_op(status)... print op op_type:status params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a status op! op_status:0 app_name:null output:null rc:0 print end perform_op(stop)... print op op_type:stop params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a stop op! op_status:0 app_name:null output:null rc:0 print end get_cur_state... current state:Busy print op op_type:start params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a start op! op_status:-1 app_name:apitest output:null rc:0 print end print op op_type:status params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a status op! op_status:-1 app_name:apitest output:null rc:0 print end print op op_type:stop params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:It is a stop op! op_status:-1 app_name:apitest output:null rc:0 print end stop_op... get_cur_state... current state:Busy print op op_type:start params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:null op_status:-1 app_name:apitest output:null rc:0 print end print op op_type:stop params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:null op_status:-1 app_name:apitest output:null rc:0 print end sleep a while... get_cur_state... current state:Idel print op op_type:stop params: 1=3ffe:ffff:0:f101::3 timeout:0 user_data:null op_status:0 app_name:apitest output:null rc:0 print end delete_rsc... signoff... Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/callbacktest.c0000644000000000000000000001202512120057602025632 0ustar00usergroup00000000000000 /* * Test program for the callback function of Local Resource Manager API. * * Copyright (C) 2004 Huang Zhen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include static void lrm_op_done_callback(lrm_op_t *op); static void printf_rsc(lrm_rsc_t *rsc); static void printf_op(lrm_op_t *op); static void printf_hash_table(GHashTable *hash_table); static gboolean lrm_dispatch(IPC_Channel *notused, gpointer user_data); static GMainLoop *mainloop; int main(int argc, char *argv[]) { ll_lrm_t* lrm; lrm_rsc_t* rsc = NULL; lrm_op_t* op = NULL; const char* rid = "ip248"; GHashTable* param = NULL; lrm = ll_lrm_new("lrm"); if(NULL == lrm) { printf("lrm==NULL\n"); return 1; } puts("sigon..."); lrm->lrm_ops->signon(lrm,"apitest"); lrm->lrm_ops->set_lrm_callback(lrm, lrm_op_done_callback); param = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(param, strdup("1"), strdup("3ffe:ffff:0:f101::3")); puts("add_rsc..."); lrm->lrm_ops->add_rsc(lrm, rid, "heartbeat", "IPv6addr", NULL, param); puts("get_rsc..."); rsc = lrm->lrm_ops->get_rsc(lrm, rid); printf_rsc(rsc); puts("perform_op(start)..."); op = lrm_op_new(); op->op_type = g_strdup("start"); op->params = NULL; op->timeout = 0; op->user_data = strdup("It is a start op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc = EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); puts("perform_op(status)..."); op = lrm_op_new(); op->op_type = g_strdup("status"); op->params = NULL; op->timeout = 0; op->user_data = strdup("It is a status op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 1000; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); puts("perform_op(stop)..."); op = lrm_op_new(); op->op_type = g_strdup("stop"); op->params = NULL; op->timeout = 0; op->user_data = strdup("It is a stop op!"); if ( op->user_data == NULL ) { fprintf(stderr, "No enough memory.\n"); return -1; } op->user_data_len = strlen(op->user_data)+1; op->interval = 0; op->target_rc=EVERYTIME; rsc->ops->perform_op(rsc,op); printf_op(op); G_main_add_IPC_Channel(G_PRIORITY_LOW, lrm->lrm_ops->ipcchan(lrm), FALSE, lrm_dispatch, lrm, NULL); mainloop = g_main_new(FALSE); g_main_run(mainloop); puts("delete_rsc..."); lrm->lrm_ops->delete_rsc(lrm, rid); puts("signoff..."); lrm->lrm_ops->signoff(lrm); return 0; } static void lrm_op_done_callback(lrm_op_t *op) { puts("lrm_op_done_callback..."); printf_op(op); } static gboolean lrm_dispatch(IPC_Channel *notused, gpointer user_data) { ll_lrm_t *lrm = (ll_lrm_t*)user_data; lrm->lrm_ops->rcvmsg(lrm, FALSE); return TRUE; } static void printf_rsc(lrm_rsc_t *rsc) { printf("print resource\n"); if (NULL == rsc) { printf("resource is null\n"); printf("print end\n"); return; } printf("\tresource of id:%s\n", rsc->id); printf("\ttype:%s\n", rsc->type); printf("\tclass:%s\n", rsc->class); printf("\tparams:\n"); printf_hash_table(rsc->params); printf("print end\n"); } static void printf_op(lrm_op_t *op) { printf("print op\n"); if (NULL == op) { printf("op is null\n"); printf("print end\n"); return; } printf("\top_type:%s\n",op->op_type?op->op_type:"null"); printf("\tparams:\n"); printf_hash_table(op->params); printf("\ttimeout:%d\n",op->timeout); printf("\tuser_data:%s\n",op->user_data?(char*)op->user_data:"null"); printf("\tuser_data pointer:%p\n",op->user_data); printf("\top_status:%d\n",op->op_status); printf("\tapp_name:%s\n",op->app_name?op->app_name:"null"); printf("\toutput:%s\n",op->output?op->output:"null"); printf("\trc:%d\n",op->rc); /* printf("\tcall_id:%d\n",op->call_id); */ printf("print end\n"); } static void printf_pair(gpointer key, gpointer value, gpointer user_data) { printf("\t\t%s=%s\n",(char*)key,(char*)value); } static void printf_hash_table(GHashTable *hash_table) { if (NULL == hash_table) { printf("\t\tnull\n"); return; } g_hash_table_foreach(hash_table, printf_pair, NULL); } Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/defaults0000644000000000000000000000030412120057602024561 0ustar00usergroup00000000000000# defaults : ${dflt_rsc:=r1} : ${dflt_type:=lrmregtest} : ${dflt_class:=ocf} : ${dflt_provider:=heartbeat} : ${dflt_timeout:=1000} : ${dflt_interval:=0} : ${dflt_targetrc:=EVERYTIME} dflt_args="" Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/descriptions0000644000000000000000000000224012120057602025461 0ustar00usergroup00000000000000lead=".TRY" describe_list() { echo $lead List resources } describe_add() { echo $lead Add resource \ ${rsc:-$dflt_rsc} \ class=${class:-$dflt_class} type=${type:-$dflt_type} \ provider=${provider:-$dflt_provider} \ args=$args } describe_del() { echo $lead Delete resource \ ${rsc:-$dflt_rsc} } describe_flush() { echo $lead Flush resource \ ${rsc:-$dflt_rsc} } describe_state() { echo $lead Show state \ ${rsc:-$dflt_rsc} } describe_info() { echo $lead Show info \ ${rsc:-$dflt_rsc} } describe_exec() { echo $lead Exec \ ${rsc:-$dflt_rsc} \ op=${operation:-$dflt_operation} \ timeout=${timeout:-$dflt_timeout} interval=${interval:-$dflt_interval} \ target=${targetrc:-$dflt_targetrc} args=$args } describe_classes() { echo $lead List classes } describe_types() { echo $lead List types \ class=${class:-$dflt_class} } describe_classmeta() { echo $lead Meta-data \ class=${class:-$dflt_class} } describe_meta() { echo $lead Show meta-data \ class=${class:-$dflt_class} \ type=${type:-$dflt_type} provider=${provider:-$dflt_provider} } describe_provider() { echo $lead Show provider \ class=${class:-$dflt_class} type=${type:-$dflt_type} } Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/evaltest.sh0000755000000000000000000000760512120057602025230 0ustar00usergroup00000000000000#!/bin/sh # Copyright (C) 2007 Dejan Muhamedagic # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # : ${TESTDIR:=testcases} : ${LRMADMIN:=../admin/lrmadmin} test -x $LRMADMIN || LRMADMIN=lrmadmin : ${OCF_ROOT:=/usr/lib/ocf} . ./defaults . ./lrmadmin-interface . ./descriptions resetvars() { unset rsc type class provider timeout interval targetrc args unset extcheck } # # special operations squad # specopt_setenv() { eval $rest } specopt_sleep() { #sleep $rest # the while loop below is the same # but we give user some feedback on what's happening while [ "$rest" -gt 0 ]; do sleep 1 echo -n "+" >&3 rest=$(($rest-1)) done } specopt_extcheck() { extcheck="$rest" set $extcheck which "$1" >/dev/null 2>&1 || # a program in the PATH extcheck="$TESTDIR/$extcheck" # or our script } specopt_repeat() { repeat_limit=$rest } specopt_bg() { if [ "$job_cnt" -gt "$bgprocs_num" ]; then bgprocs_num=${rest:-1} job_cnt=1 else echo ".BG bad usage: more tests yet to be backgrounded" fi } specopt_bgrepeat() { # common specopt_bg specopt_repeat } specopt_wait() { # common waitforbgprocs } specopt_shell() { # run command with shell echo "$rest" | sh -s | # and execute the command { [ "$extcheck" ] && $extcheck || cat;} } specopt() { cmd=`echo $cmd | sed 's/%//'` # strip leading '%' echo ".`echo $cmd | tr '[a-z]' '[A-Z]'` $rest" # show what we got specopt_$cmd # do what they asked for } # # wait for background processes to finish # and print their output # NB: We wait for processes in a FIFO order # The order in which they finish does not matter # waitforbgprocs() { while [ "$bgprocs" ]; do set $bgprocs proc=$1 # get the first one shift 1 # remove it from the list bgprocs="$@" IFS=":" set $proc # split into lineno,pid testline=$1 jobnum=$2 pid=$3 unset IFS while kill -0 $pid 2>/dev/null; do sleep 1 done wait $pid # capture the exit code echo ".BG test line $testline/job $jobnum finished (exit code: $?):" echo "==========test:$testline:$jobnum start output==========" cat $OUTDIR/bg$$-$testline-$jobnum echo "==========test:$testline:$jobnum end output==========" rm -f $OUTDIR/bg$$-$testline-$jobnum done } # # substitute variables in the test line # substvars() { sed " s/%t/$test_cnt/g s/%l/$line/g s/%j/$job_cnt/g s/%i/$repeat_cnt/g " } dotest() { echo -n "." >&3 test_cnt=$(($test_cnt+1)) describe_$cmd # show what we are about to do lrm_$cmd | # and execute the command { [ "$extcheck" ] && $extcheck || cat;} } runonetest() { eval `echo $rest | substvars` # set parameters if [ "$job_cnt" -le "$bgprocs_num" ]; then echo .BG test line $line/job $job_cnt runs in background dotest > $OUTDIR/bg$$-$line-$job_cnt 2>&1 & bgprocs="$bgprocs $line:$job_cnt:$!" job_cnt=$(($job_cnt+1)) else dotest fi } runtest() { while [ $repeat_cnt -le $repeat_limit ]; do runonetest resetvars # unset all variables repeat_cnt=$(($repeat_cnt+1)) done repeat_limit=1 repeat_cnt=1 } # # run the tests # bgprocs_num=0 job_cnt=1 repeat_limit=1 repeat_cnt=1 line=1 test_cnt=1 while read cmd rest; do case "$cmd" in "") : empty ;; "#"*) : a comment ;; "%stop") break ;; "%"*) specopt ;; *) runtest ;; esac line=$(($line+1)) done waitforbgprocs Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/language0000644000000000000000000000035512120057602024543 0ustar00usergroup00000000000000The meta language and how it translates to the lrmadmin options: list:-L add:-A %r %C %T %P del:-D %r flush:-F %r state:-S %r info:-I %r exec:-E %r %o %t %i %e classes:-C types:-T %C classmeta:-O %C meta:-M %C %T %P provider:-P %C %T Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/lrmadmin-interface0000644000000000000000000000151412120057602026517 0ustar00usergroup00000000000000lrm_list() { $LRMADMIN -L } lrm_add() { $LRMADMIN -A ${rsc:-$dflt_rsc} \ ${class:-$dflt_class} ${type:-$dflt_type} \ ${provider:-$dflt_provider} \ $args } lrm_del() { $LRMADMIN -D ${rsc:-$dflt_rsc} } lrm_flush() { $LRMADMIN -F ${rsc:-$dflt_rsc} } lrm_state() { $LRMADMIN -S ${rsc:-$dflt_rsc} } lrm_info() { $LRMADMIN -I ${rsc:-$dflt_rsc} } lrm_exec() { $LRMADMIN -E ${rsc:-$dflt_rsc} \ ${operation:-$dflt_operation} \ ${timeout:-$dflt_timeout} ${interval:-$dflt_interval} \ ${targetrc:-$dflt_targetrc} $args } lrm_classes() { $LRMADMIN -C } lrm_types() { $LRMADMIN -T ${class:-$dflt_class} } lrm_classmeta() { $LRMADMIN -O ${class:-$dflt_class} } lrm_meta() { $LRMADMIN -M ${class:-$dflt_class} ${type:-$dflt_type} ${provider:-$dflt_provider} } lrm_provider() { $LRMADMIN -P ${class:-$dflt_class} ${type:-$dflt_type} } Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/lrmregtest-lsb0000644000000000000000000000200412120057602025717 0ustar00usergroup00000000000000#!/bin/sh # # WARNING: This script is for LRM regressions tests only # ### BEGIN INIT INFO # Provides: lrmregtest # Required-Start: # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: # Default-Stop: # Short-Description: LRM regression tests LSB RA # Description: LRM regression tests LSB RA ### END INIT INFO TYPE=lrmregtest # depends on resource-agents and the OCF : ${OCF_ROOT:=/usr/lib/ocf} . ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs case "$1" in start) echo -n "Starting $TYPE" ha_pseudo_resource lrmregtest_lsb start ;; stop) echo -n "Shutting down $TYPE" ha_pseudo_resource lrmregtest_lsb stop ;; status) echo -n "Checking for $TYPE" ha_pseudo_resource lrmregtest_lsb monitor if [ $? -eq 0 ]; then echo " running" exit 0 else echo " stopped" exit 3 fi ;; *) echo "Usage: $0 {start|stop|status}" exit 1 ;; esac if [ $? -eq 0 ]; then echo " OK" exit 0 else echo " failed" exit 1 fi Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/lrmregtest.in0000644000000000000000000001340312120057602025553 0ustar00usergroup00000000000000#!/bin/sh # # # lrmregtest OCF RA. Does nothing but wait a few seconds, can be # configured to fail occassionally. # # updated to support the LRM regression testing. # # Copyright (c) 2007 SUSE LINUX AG, Dejan Muhamedagic # All Rights Reserved. # # Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # Initialization: . ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs ####################################################################### meta_data() { cat < 1.0 This is a lrmregtest Resource Agent. Use for LRM regression testing. lrmregtest resource agent How long to delay before each action. Action delay Complain loudly if they try to run us in parallel on the same resource. Report error if run twice at the same time Process the TERM signal and don't exit. No TERM ain't gonna kill us. Print more information. Be verbose. END } ####################################################################### # don't exit on TERM, to test that lrmd makes sure that we do exit sigterm_handler() { ocf_log info "They use TERM to bring us down. No such luck." return } dummy_usage() { cat </dev/null then ocf_log err "There is another instance of ${OCF_RESOURCE_INSTANCE} running: pid `cat $lockf`." exit $OCF_ERR_GENERIC fi } [ "$OCF_RESKEY_check_parallel" = 1 ] && check4parallel [ "$OCF_RESKEY_ignore_TERM" = 1 ] && trap sigterm_handler TERM echo $$ > $lockf trap "rm -f $lockf" EXIT verbose && invocation $@ case $__OCF_ACTION in meta-data) meta_data exit $OCF_SUCCESS ;; start) dummy_start;; stop) dummy_stop;; monitor) dummy_monitor;; migrate_to) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_to}." dummy_stop ;; migrate_from) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrated_from}." dummy_start ;; reload) ocf_log err "Reloading..." dummy_start ;; validate-all) dummy_validate;; usage|help) dummy_usage exit $OCF_SUCCESS ;; *) dummy_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac rc=$? ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc" exit $rc Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/plugintest.c0000644000000000000000000000517712120057602025406 0ustar00usergroup00000000000000/* File: plugintest.c * Description: A small,simple tool to test RA execution plugin * * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * Todo: security verification * * 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.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include static void g_print_item(gpointer data, gpointer user_data) { printf("%s\n", (char*)data); } int main(void) { PILPluginUniv * PluginLoadingSystem = NULL; GHashTable * RAExecFuncs = NULL; GList * ratype_list; struct RAExecOps * RAExec; /* GHashTable * cmd_params; */ int ret; PILGenericIfMgmtRqst RegisterRqsts[]= { {"RAExec", &RAExecFuncs, NULL, NULL, NULL}, { NULL, NULL, NULL, NULL, NULL} }; PluginLoadingSystem = NewPILPluginUniv ("/usr/lib/heartbeat/plugins"); PILLoadPlugin(PluginLoadingSystem , "InterfaceMgr", "generic" , &RegisterRqsts); PILLoadPlugin(PluginLoadingSystem , "RAExec", "ocf", NULL); RAExec = g_hash_table_lookup(RAExecFuncs,"ocf"); ret = RAExec->get_resource_list(&ratype_list); printf("length=%d\n", g_list_length(ratype_list)); if (ret >= 0) { g_list_foreach(ratype_list, g_print_item, NULL); } /* PILLoadPlugin(PluginLoadingSystem , "RAExec", "lsb", NULL); RAExec = g_hash_table_lookup(RAExecFuncs,"lsb"); cmd_params = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(cmd_params, g_strdup("1"), g_strdup("par1")); g_hash_table_insert(cmd_params, g_strdup("2"), g_strdup("par2")); ret = RAExec->execra("/tmp/test.sh", "start", cmd_params,NULL); */ /* For test the dealing with directory appended to RA */ /* PILLoadPlugin(PluginLoadingSystem , "RAExec", "ocf", NULL); RAExec = g_hash_table_lookup(RAExecFuncs,"ocf"); if (0>RAExec->execra("/root/linux-ha-checkout/linux-ha/lrm/test.sh", "stop",NULL,NULL, TRUE, &key)) */ printf("execra result: ret = %d\n", ret); return -1; } Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/regression.sh.in0000755000000000000000000001262512120057602026164 0ustar00usergroup00000000000000#!/bin/sh # Copyright (C) 2007 Dejan Muhamedagic # # 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.1 of the License, or (at your option) any later version. # # This software 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # OCF_ROOT=@OCF_ROOT_DIR@ export OCF_ROOT if [ -z "$OCF_ROOT" ]; then [ -d /usr/lib/ocf ] && OCF_ROOT=/usr/lib/ocf fi if [ ! -d "$OCF_ROOT" ]; then echo "OCF_ROOT environment variable not set" exit 2 fi TESTDIR=${TESTDIR:-testcases} DFLT_TESTSET=basicset OUTDIR=${OUTDIR:-output} LRMD_OUTF="$OUTDIR/lrmd.out" LRMD_LOGF="$OUTDIR/lrmd.log" LRMD_DEBUGF="$OUTDIR/lrmd.debug" OUTF="$OUTDIR/regression.out" LRMADMIN="@sbindir@/lrmadmin" LRMD_OPTS="-vvv" STONITHD_OPTS="-at" DIFF_OPTS="--ignore-all-space -U 1" common_filter=$TESTDIR/common.filter common_exclf=$TESTDIR/common.excl OCF_RA=$OCF_ROOT/resource.d/heartbeat/lrmregtest LSB_RA=@LSB_RA_DIR@/lrmregtest export OUTDIR TESTDIR LRMADMIN logmsg() { echo "`date`: $*" | tee -a $LRMD_DEBUGF | tee -a $LRMD_LOGF } abspath() { echo $1 | grep -qs "^/" && echo $1 || echo `pwd`/$1 } usage() { cat</dev/null 2>&1 || { echo "WARNING: xmllint not available, some of the tests may fail" } rm -f $LRMD_LOGF $LRMD_DEBUGF # make lrmd log to our files only HA_logfile=`abspath $LRMD_LOGF` HA_debugfile=`abspath $LRMD_DEBUGF` HA_use_logd=no HA_logfacility="" export HA_logfile HA_debugfile HA_use_logd HA_logfacility mkdir -p $OUTDIR . ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs args=`getopt hq $*` [ $? -ne 0 ] && usage eval set -- "$args" SILENT="" while [ x"$1" != x ]; do case "$1" in -h) usage;; -q) SILENT=1;; --) shift 1; break;; *) usage;; esac shift 1 done exec >$OUTF 2>&1 if [ "$SILENT" = 1 ]; then exec 3>/dev/null else exec 3>/dev/tty fi start_stonithd() { echo "starting stonithd" >&3 $HA_BIN/stonithd -s 2>/dev/null if [ $? -ne 0 ]; then STOP_STONITHD=1 $HA_BIN/stonithd $STONITHD_OPTS sleep 1 $HA_BIN/stonithd -s 2>/dev/null else STOP_STONITHD= fi } stop_stonithd() { if [ "$STOP_STONITHD" ]; then echo "stopping stonithd" >&3 $HA_BIN/stonithd -k >/dev/null 2>&1 fi } start_lrmd() { echo "starting lrmd" >&3 $HA_BIN/lrmd -s 2>/dev/null if [ $? -eq 3 ]; then #strace -o /tmp/lrmd.trc $HA_BIN/lrmd $LRMD_OPTS >$LRMD_OUTF 2>&1 & $HA_BIN/lrmd $LRMD_OPTS >$LRMD_OUTF 2>&1 & sleep 1 $HA_BIN/lrmd -s 2>/dev/null else echo "lrmd already running; can't proceed" >&3 return 2 fi } stop_lrmd() { echo "stopping lrmd" >&3 $HA_BIN/lrmd -k } cp_ra() { cp -p lrmregtest $OCF_RA chmod +x $OCF_RA cp -p lrmregtest-lsb $LSB_RA chmod +x $LSB_RA } rm_ra() { rm -f $OCF_RA $LSB_RA } cp_ra start_lrmd || exit $? # start_stonithd || exit $? trap "stop_lrmd; stop_stonithd; rm_ra" EXIT setenvironment() { filterf=$TESTDIR/$testcase.filter exclf=$TESTDIR/$testcase.excl log_filter=$TESTDIR/$testcase.log_filter expf=$TESTDIR/$testcase.exp outf=$OUTDIR/$testcase.out difff=$OUTDIR/$testcase.diff } filter_output() { { [ -x $common_filter ] && $common_filter || cat;} | { [ -f $common_exclf ] && egrep -vf $common_exclf || cat;} | { [ -x $filterf ] && $filterf || cat;} | { [ -f $exclf ] && egrep -vf $exclf || cat;} } dumpcase() { cat<&3 logmsg "BEGIN testcase $testcase" ./evaltest.sh < $TESTDIR/$testcase > $outf 2>&1 filter_output < $outf | if [ "$prepare" ]; then echo " saving to expect file" >&3 cat > $expf else echo -n " checking..." >&3 diff $DIFF_OPTS $expf - > $difff if [ $? -ne 0 ]; then echo " FAIL" >&3 dumpcase return 1 else echo " PASS" >&3 rm -f $outf $difff fi fi sed -n "/BEGIN testcase $testcase/,\$p" $LRMD_LOGF | { [ -x $log_filter ] && $log_filter || cat;} | egrep '(CRIT|ERROR):' logmsg "END testcase $testcase" } [ "$1" = prepare ] && { prepare=1; shift 1;} [ $# -eq 0 ] && set "set:$DFLT_TESTSET" for a; do if [ "$a" -a -f "$TESTDIR/$a" ]; then testcase=$a runtestcase else echo "$a" | grep -q "^set:" && TESTSET=$TESTDIR/`echo $a | sed 's/set://'` while read testcase; do runtestcase done < $TESTSET fi done if egrep -wv '(BEGIN|END) testcase' $OUTF >/dev/null then echo "seems like some tests failed or else something not expected" echo "check $OUTF and diff files in $OUTDIR" echo "in case you wonder what lrmd was doing, read $LRMD_LOGF and $LRMD_DEBUGF" exit 1 else rm -f $OUTF $LRMD_OUTF fi >&3 Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/BSC0000644000000000000000000000004112120057602025355 0ustar00usergroup00000000000000rscmgmt metadata rscexec stonith Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/Makefile.am0000644000000000000000000000225012120057602027063 0ustar00usergroup00000000000000# # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in testcasesdir = $(datadir)/$(PACKAGE_NAME)/lrmtest/testcases testcases_SCRIPTS = common.filter ra-list.sh rscmgmt.log_filter xmllint.sh testcases_DATA = BSC basicset metadata metadata.exp rscexec \ rscexec.exp rscmgmt rscmgmt.exp \ stonith stonith.exp # shouldn't need this next line... EXTRA_DIST = $(testcases_SCRIPTS) $(testcases_DATA) Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/basicset0000644000000000000000000000006112120057602026545 0ustar00usergroup00000000000000rscmgmt metadata rscexec stonith serialize flood Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/common.filter0000755000000000000000000000146212120057602027535 0ustar00usergroup00000000000000#!/bin/sh sed ' /^lrmadmin/s/([0-9][0-9]*)/()/ /^lrmadmin/s/^lrmadmin[^:]*: [^ ]* // s/call_id=[0-9][0-9]*/call_id=(removed)/ /run at:/d /last rc change at:/d /queue time:/d ' | awk ' /Waiting for lrmd to callback.../ { n=1; next; } n==1 && /----------------operation--------------/ { n++; next; } n==2 && /type:/ { op=$0; sub("type:","",op); next } n==2 && /operation status:/ { desc=$0; sub("operation status:","",desc); next } n==2 && /op_status:/ { stat=$0; sub("op_status: *","",stat); next } n==2 && /return code:/ { rc=$0; sub("return code: *","",rc); next } n==2 && /output data:/ { n++; next; } n==3 && /---------------------------------------/ { printf("> %s %s (status=%s,rc=%s): %s\n",op,desc,stat,rc,substr(output,2)); n=0; output=""; next; } n==3 && $1!="" { output=output"/"$0; next; } { print } ' Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/flood0000644000000000000000000000060712120057602026061 0ustar00usergroup00000000000000# 30 secs should be enough even on slow machines list %setenv dflt_timeout=30000 # add 64 resources %repeat 64 add rsc=r%i args="delay=0" # start all in background %bgrepeat 64 exec rsc=r%i operation=start %sleep 1 # and run a monitor on all in background %bgrepeat 64 exec rsc=r%i operation=monitor %sleep 1 # finally, stop all %repeat 64 exec rsc=r%i operation=stop %repeat 64 del rsc=r%i Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/flood.exp0000644000000000000000000015476212120057602026670 0ustar00usergroup00000000000000.TRY List resources Currently no resources are managed by LRM. .SETENV dflt_timeout=30000 .REPEAT 64 .TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r3 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r4 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r5 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r6 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r7 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r8 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r9 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r10 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r11 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r12 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r13 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r14 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r15 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r16 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r17 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r18 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r19 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r20 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r21 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r22 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r23 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r24 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r25 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r26 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r27 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r28 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r29 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r30 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r31 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r32 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r33 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r34 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r35 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r36 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r37 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r38 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r39 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r40 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r41 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r42 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r43 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r44 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r45 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r46 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r47 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r48 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r49 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r50 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r51 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r52 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r53 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r54 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r55 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r56 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r57 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r58 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r59 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r60 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r61 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r62 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r63 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY Add resource r64 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .BGREPEAT 64 .BG test line 9/job 1 runs in background .BG test line 9/job 2 runs in background .BG test line 9/job 3 runs in background .BG test line 9/job 4 runs in background .BG test line 9/job 5 runs in background .BG test line 9/job 6 runs in background .BG test line 9/job 7 runs in background .BG test line 9/job 8 runs in background .BG test line 9/job 9 runs in background .BG test line 9/job 10 runs in background .BG test line 9/job 11 runs in background .BG test line 9/job 12 runs in background .BG test line 9/job 13 runs in background .BG test line 9/job 14 runs in background .BG test line 9/job 15 runs in background .BG test line 9/job 16 runs in background .BG test line 9/job 17 runs in background .BG test line 9/job 18 runs in background .BG test line 9/job 19 runs in background .BG test line 9/job 20 runs in background .BG test line 9/job 21 runs in background .BG test line 9/job 22 runs in background .BG test line 9/job 23 runs in background .BG test line 9/job 24 runs in background .BG test line 9/job 25 runs in background .BG test line 9/job 26 runs in background .BG test line 9/job 27 runs in background .BG test line 9/job 28 runs in background .BG test line 9/job 29 runs in background .BG test line 9/job 30 runs in background .BG test line 9/job 31 runs in background .BG test line 9/job 32 runs in background .BG test line 9/job 33 runs in background .BG test line 9/job 34 runs in background .BG test line 9/job 35 runs in background .BG test line 9/job 36 runs in background .BG test line 9/job 37 runs in background .BG test line 9/job 38 runs in background .BG test line 9/job 39 runs in background .BG test line 9/job 40 runs in background .BG test line 9/job 41 runs in background .BG test line 9/job 42 runs in background .BG test line 9/job 43 runs in background .BG test line 9/job 44 runs in background .BG test line 9/job 45 runs in background .BG test line 9/job 46 runs in background .BG test line 9/job 47 runs in background .BG test line 9/job 48 runs in background .BG test line 9/job 49 runs in background .BG test line 9/job 50 runs in background .BG test line 9/job 51 runs in background .BG test line 9/job 52 runs in background .BG test line 9/job 53 runs in background .BG test line 9/job 54 runs in background .BG test line 9/job 55 runs in background .BG test line 9/job 56 runs in background .BG test line 9/job 57 runs in background .BG test line 9/job 58 runs in background .BG test line 9/job 59 runs in background .BG test line 9/job 60 runs in background .BG test line 9/job 61 runs in background .BG test line 9/job 62 runs in background .BG test line 9/job 63 runs in background .BG test line 9/job 64 runs in background .SLEEP 1 .BGREPEAT 64 .BG test line 13/job 1 runs in background .BG test line 13/job 2 runs in background .BG test line 13/job 3 runs in background .BG test line 13/job 4 runs in background .BG test line 13/job 5 runs in background .BG test line 13/job 6 runs in background .BG test line 13/job 7 runs in background .BG test line 13/job 8 runs in background .BG test line 13/job 9 runs in background .BG test line 13/job 10 runs in background .BG test line 13/job 11 runs in background .BG test line 13/job 12 runs in background .BG test line 13/job 13 runs in background .BG test line 13/job 14 runs in background .BG test line 13/job 15 runs in background .BG test line 13/job 16 runs in background .BG test line 13/job 17 runs in background .BG test line 13/job 18 runs in background .BG test line 13/job 19 runs in background .BG test line 13/job 20 runs in background .BG test line 13/job 21 runs in background .BG test line 13/job 22 runs in background .BG test line 13/job 23 runs in background .BG test line 13/job 24 runs in background .BG test line 13/job 25 runs in background .BG test line 13/job 26 runs in background .BG test line 13/job 27 runs in background .BG test line 13/job 28 runs in background .BG test line 13/job 29 runs in background .BG test line 13/job 30 runs in background .BG test line 13/job 31 runs in background .BG test line 13/job 32 runs in background .BG test line 13/job 33 runs in background .BG test line 13/job 34 runs in background .BG test line 13/job 35 runs in background .BG test line 13/job 36 runs in background .BG test line 13/job 37 runs in background .BG test line 13/job 38 runs in background .BG test line 13/job 39 runs in background .BG test line 13/job 40 runs in background .BG test line 13/job 41 runs in background .BG test line 13/job 42 runs in background .BG test line 13/job 43 runs in background .BG test line 13/job 44 runs in background .BG test line 13/job 45 runs in background .BG test line 13/job 46 runs in background .BG test line 13/job 47 runs in background .BG test line 13/job 48 runs in background .BG test line 13/job 49 runs in background .BG test line 13/job 50 runs in background .BG test line 13/job 51 runs in background .BG test line 13/job 52 runs in background .BG test line 13/job 53 runs in background .BG test line 13/job 54 runs in background .BG test line 13/job 55 runs in background .BG test line 13/job 56 runs in background .BG test line 13/job 57 runs in background .BG test line 13/job 58 runs in background .BG test line 13/job 59 runs in background .BG test line 13/job 60 runs in background .BG test line 13/job 61 runs in background .BG test line 13/job 62 runs in background .BG test line 13/job 63 runs in background .BG test line 13/job 64 runs in background .SLEEP 1 .REPEAT 64 .TRY Exec r1 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r2 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r3 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r4 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r5 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r6 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r7 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r8 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r9 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r10 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r11 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r12 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r13 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r14 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r15 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r16 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r17 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r18 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r19 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r20 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r21 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r22 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r23 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r24 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r25 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r26 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r27 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r28 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r29 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r30 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r31 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r32 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r33 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r34 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r35 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r36 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r37 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r38 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r39 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r40 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r41 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r42 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r43 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r44 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r45 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r46 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r47 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r48 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r49 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r50 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r51 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r52 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r53 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r54 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r55 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r56 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r57 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r58 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r59 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r60 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r61 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r62 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r63 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec r64 op=stop timeout=30000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .REPEAT 64 .TRY Delete resource r1 Succeeded in deleting this resource. .TRY Delete resource r2 Succeeded in deleting this resource. .TRY Delete resource r3 Succeeded in deleting this resource. .TRY Delete resource r4 Succeeded in deleting this resource. .TRY Delete resource r5 Succeeded in deleting this resource. .TRY Delete resource r6 Succeeded in deleting this resource. .TRY Delete resource r7 Succeeded in deleting this resource. .TRY Delete resource r8 Succeeded in deleting this resource. .TRY Delete resource r9 Succeeded in deleting this resource. .TRY Delete resource r10 Succeeded in deleting this resource. .TRY Delete resource r11 Succeeded in deleting this resource. .TRY Delete resource r12 Succeeded in deleting this resource. .TRY Delete resource r13 Succeeded in deleting this resource. .TRY Delete resource r14 Succeeded in deleting this resource. .TRY Delete resource r15 Succeeded in deleting this resource. .TRY Delete resource r16 Succeeded in deleting this resource. .TRY Delete resource r17 Succeeded in deleting this resource. .TRY Delete resource r18 Succeeded in deleting this resource. .TRY Delete resource r19 Succeeded in deleting this resource. .TRY Delete resource r20 Succeeded in deleting this resource. .TRY Delete resource r21 Succeeded in deleting this resource. .TRY Delete resource r22 Succeeded in deleting this resource. .TRY Delete resource r23 Succeeded in deleting this resource. .TRY Delete resource r24 Succeeded in deleting this resource. .TRY Delete resource r25 Succeeded in deleting this resource. .TRY Delete resource r26 Succeeded in deleting this resource. .TRY Delete resource r27 Succeeded in deleting this resource. .TRY Delete resource r28 Succeeded in deleting this resource. .TRY Delete resource r29 Succeeded in deleting this resource. .TRY Delete resource r30 Succeeded in deleting this resource. .TRY Delete resource r31 Succeeded in deleting this resource. .TRY Delete resource r32 Succeeded in deleting this resource. .TRY Delete resource r33 Succeeded in deleting this resource. .TRY Delete resource r34 Succeeded in deleting this resource. .TRY Delete resource r35 Succeeded in deleting this resource. .TRY Delete resource r36 Succeeded in deleting this resource. .TRY Delete resource r37 Succeeded in deleting this resource. .TRY Delete resource r38 Succeeded in deleting this resource. .TRY Delete resource r39 Succeeded in deleting this resource. .TRY Delete resource r40 Succeeded in deleting this resource. .TRY Delete resource r41 Succeeded in deleting this resource. .TRY Delete resource r42 Succeeded in deleting this resource. .TRY Delete resource r43 Succeeded in deleting this resource. .TRY Delete resource r44 Succeeded in deleting this resource. .TRY Delete resource r45 Succeeded in deleting this resource. .TRY Delete resource r46 Succeeded in deleting this resource. .TRY Delete resource r47 Succeeded in deleting this resource. .TRY Delete resource r48 Succeeded in deleting this resource. .TRY Delete resource r49 Succeeded in deleting this resource. .TRY Delete resource r50 Succeeded in deleting this resource. .TRY Delete resource r51 Succeeded in deleting this resource. .TRY Delete resource r52 Succeeded in deleting this resource. .TRY Delete resource r53 Succeeded in deleting this resource. .TRY Delete resource r54 Succeeded in deleting this resource. .TRY Delete resource r55 Succeeded in deleting this resource. .TRY Delete resource r56 Succeeded in deleting this resource. .TRY Delete resource r57 Succeeded in deleting this resource. .TRY Delete resource r58 Succeeded in deleting this resource. .TRY Delete resource r59 Succeeded in deleting this resource. .TRY Delete resource r60 Succeeded in deleting this resource. .TRY Delete resource r61 Succeeded in deleting this resource. .TRY Delete resource r62 Succeeded in deleting this resource. .TRY Delete resource r63 Succeeded in deleting this resource. .TRY Delete resource r64 Succeeded in deleting this resource. .BG test line 9/job 1 finished (exit code: 0): ==========test:9:1 start output========== .TRY Exec r1 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:1 end output========== .BG test line 9/job 2 finished (exit code: 0): ==========test:9:2 start output========== .TRY Exec r2 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:2 end output========== .BG test line 9/job 3 finished (exit code: 0): ==========test:9:3 start output========== .TRY Exec r3 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:3 end output========== .BG test line 9/job 4 finished (exit code: 0): ==========test:9:4 start output========== .TRY Exec r4 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:4 end output========== .BG test line 9/job 5 finished (exit code: 0): ==========test:9:5 start output========== .TRY Exec r5 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:5 end output========== .BG test line 9/job 6 finished (exit code: 0): ==========test:9:6 start output========== .TRY Exec r6 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:6 end output========== .BG test line 9/job 7 finished (exit code: 0): ==========test:9:7 start output========== .TRY Exec r7 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:7 end output========== .BG test line 9/job 8 finished (exit code: 0): ==========test:9:8 start output========== .TRY Exec r8 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:8 end output========== .BG test line 9/job 9 finished (exit code: 0): ==========test:9:9 start output========== .TRY Exec r9 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:9 end output========== .BG test line 9/job 10 finished (exit code: 0): ==========test:9:10 start output========== .TRY Exec r10 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:10 end output========== .BG test line 9/job 11 finished (exit code: 0): ==========test:9:11 start output========== .TRY Exec r11 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:11 end output========== .BG test line 9/job 12 finished (exit code: 0): ==========test:9:12 start output========== .TRY Exec r12 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:12 end output========== .BG test line 9/job 13 finished (exit code: 0): ==========test:9:13 start output========== .TRY Exec r13 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:13 end output========== .BG test line 9/job 14 finished (exit code: 0): ==========test:9:14 start output========== .TRY Exec r14 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:14 end output========== .BG test line 9/job 15 finished (exit code: 0): ==========test:9:15 start output========== .TRY Exec r15 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:15 end output========== .BG test line 9/job 16 finished (exit code: 0): ==========test:9:16 start output========== .TRY Exec r16 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:16 end output========== .BG test line 9/job 17 finished (exit code: 0): ==========test:9:17 start output========== .TRY Exec r17 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:17 end output========== .BG test line 9/job 18 finished (exit code: 0): ==========test:9:18 start output========== .TRY Exec r18 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:18 end output========== .BG test line 9/job 19 finished (exit code: 0): ==========test:9:19 start output========== .TRY Exec r19 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:19 end output========== .BG test line 9/job 20 finished (exit code: 0): ==========test:9:20 start output========== .TRY Exec r20 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:20 end output========== .BG test line 9/job 21 finished (exit code: 0): ==========test:9:21 start output========== .TRY Exec r21 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:21 end output========== .BG test line 9/job 22 finished (exit code: 0): ==========test:9:22 start output========== .TRY Exec r22 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:22 end output========== .BG test line 9/job 23 finished (exit code: 0): ==========test:9:23 start output========== .TRY Exec r23 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:23 end output========== .BG test line 9/job 24 finished (exit code: 0): ==========test:9:24 start output========== .TRY Exec r24 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:24 end output========== .BG test line 9/job 25 finished (exit code: 0): ==========test:9:25 start output========== .TRY Exec r25 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:25 end output========== .BG test line 9/job 26 finished (exit code: 0): ==========test:9:26 start output========== .TRY Exec r26 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:26 end output========== .BG test line 9/job 27 finished (exit code: 0): ==========test:9:27 start output========== .TRY Exec r27 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:27 end output========== .BG test line 9/job 28 finished (exit code: 0): ==========test:9:28 start output========== .TRY Exec r28 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:28 end output========== .BG test line 9/job 29 finished (exit code: 0): ==========test:9:29 start output========== .TRY Exec r29 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:29 end output========== .BG test line 9/job 30 finished (exit code: 0): ==========test:9:30 start output========== .TRY Exec r30 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:30 end output========== .BG test line 9/job 31 finished (exit code: 0): ==========test:9:31 start output========== .TRY Exec r31 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:31 end output========== .BG test line 9/job 32 finished (exit code: 0): ==========test:9:32 start output========== .TRY Exec r32 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:32 end output========== .BG test line 9/job 33 finished (exit code: 0): ==========test:9:33 start output========== .TRY Exec r33 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:33 end output========== .BG test line 9/job 34 finished (exit code: 0): ==========test:9:34 start output========== .TRY Exec r34 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:34 end output========== .BG test line 9/job 35 finished (exit code: 0): ==========test:9:35 start output========== .TRY Exec r35 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:35 end output========== .BG test line 9/job 36 finished (exit code: 0): ==========test:9:36 start output========== .TRY Exec r36 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:36 end output========== .BG test line 9/job 37 finished (exit code: 0): ==========test:9:37 start output========== .TRY Exec r37 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:37 end output========== .BG test line 9/job 38 finished (exit code: 0): ==========test:9:38 start output========== .TRY Exec r38 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:38 end output========== .BG test line 9/job 39 finished (exit code: 0): ==========test:9:39 start output========== .TRY Exec r39 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:39 end output========== .BG test line 9/job 40 finished (exit code: 0): ==========test:9:40 start output========== .TRY Exec r40 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:40 end output========== .BG test line 9/job 41 finished (exit code: 0): ==========test:9:41 start output========== .TRY Exec r41 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:41 end output========== .BG test line 9/job 42 finished (exit code: 0): ==========test:9:42 start output========== .TRY Exec r42 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:42 end output========== .BG test line 9/job 43 finished (exit code: 0): ==========test:9:43 start output========== .TRY Exec r43 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:43 end output========== .BG test line 9/job 44 finished (exit code: 0): ==========test:9:44 start output========== .TRY Exec r44 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:44 end output========== .BG test line 9/job 45 finished (exit code: 0): ==========test:9:45 start output========== .TRY Exec r45 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:45 end output========== .BG test line 9/job 46 finished (exit code: 0): ==========test:9:46 start output========== .TRY Exec r46 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:46 end output========== .BG test line 9/job 47 finished (exit code: 0): ==========test:9:47 start output========== .TRY Exec r47 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:47 end output========== .BG test line 9/job 48 finished (exit code: 0): ==========test:9:48 start output========== .TRY Exec r48 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:48 end output========== .BG test line 9/job 49 finished (exit code: 0): ==========test:9:49 start output========== .TRY Exec r49 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:49 end output========== .BG test line 9/job 50 finished (exit code: 0): ==========test:9:50 start output========== .TRY Exec r50 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:50 end output========== .BG test line 9/job 51 finished (exit code: 0): ==========test:9:51 start output========== .TRY Exec r51 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:51 end output========== .BG test line 9/job 52 finished (exit code: 0): ==========test:9:52 start output========== .TRY Exec r52 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:52 end output========== .BG test line 9/job 53 finished (exit code: 0): ==========test:9:53 start output========== .TRY Exec r53 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:53 end output========== .BG test line 9/job 54 finished (exit code: 0): ==========test:9:54 start output========== .TRY Exec r54 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:54 end output========== .BG test line 9/job 55 finished (exit code: 0): ==========test:9:55 start output========== .TRY Exec r55 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:55 end output========== .BG test line 9/job 56 finished (exit code: 0): ==========test:9:56 start output========== .TRY Exec r56 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:56 end output========== .BG test line 9/job 57 finished (exit code: 0): ==========test:9:57 start output========== .TRY Exec r57 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:57 end output========== .BG test line 9/job 58 finished (exit code: 0): ==========test:9:58 start output========== .TRY Exec r58 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:58 end output========== .BG test line 9/job 59 finished (exit code: 0): ==========test:9:59 start output========== .TRY Exec r59 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:59 end output========== .BG test line 9/job 60 finished (exit code: 0): ==========test:9:60 start output========== .TRY Exec r60 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:60 end output========== .BG test line 9/job 61 finished (exit code: 0): ==========test:9:61 start output========== .TRY Exec r61 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:61 end output========== .BG test line 9/job 62 finished (exit code: 0): ==========test:9:62 start output========== .TRY Exec r62 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:62 end output========== .BG test line 9/job 63 finished (exit code: 0): ==========test:9:63 start output========== .TRY Exec r63 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:63 end output========== .BG test line 9/job 64 finished (exit code: 0): ==========test:9:64 start output========== .TRY Exec r64 op=start timeout=30000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:9:64 end output========== .BG test line 13/job 1 finished (exit code: 0): ==========test:13:1 start output========== .TRY Exec r1 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:1 end output========== .BG test line 13/job 2 finished (exit code: 0): ==========test:13:2 start output========== .TRY Exec r2 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:2 end output========== .BG test line 13/job 3 finished (exit code: 0): ==========test:13:3 start output========== .TRY Exec r3 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:3 end output========== .BG test line 13/job 4 finished (exit code: 0): ==========test:13:4 start output========== .TRY Exec r4 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:4 end output========== .BG test line 13/job 5 finished (exit code: 0): ==========test:13:5 start output========== .TRY Exec r5 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:5 end output========== .BG test line 13/job 6 finished (exit code: 0): ==========test:13:6 start output========== .TRY Exec r6 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:6 end output========== .BG test line 13/job 7 finished (exit code: 0): ==========test:13:7 start output========== .TRY Exec r7 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:7 end output========== .BG test line 13/job 8 finished (exit code: 0): ==========test:13:8 start output========== .TRY Exec r8 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:8 end output========== .BG test line 13/job 9 finished (exit code: 0): ==========test:13:9 start output========== .TRY Exec r9 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:9 end output========== .BG test line 13/job 10 finished (exit code: 0): ==========test:13:10 start output========== .TRY Exec r10 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:10 end output========== .BG test line 13/job 11 finished (exit code: 0): ==========test:13:11 start output========== .TRY Exec r11 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:11 end output========== .BG test line 13/job 12 finished (exit code: 0): ==========test:13:12 start output========== .TRY Exec r12 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:12 end output========== .BG test line 13/job 13 finished (exit code: 0): ==========test:13:13 start output========== .TRY Exec r13 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:13 end output========== .BG test line 13/job 14 finished (exit code: 0): ==========test:13:14 start output========== .TRY Exec r14 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:14 end output========== .BG test line 13/job 15 finished (exit code: 0): ==========test:13:15 start output========== .TRY Exec r15 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:15 end output========== .BG test line 13/job 16 finished (exit code: 0): ==========test:13:16 start output========== .TRY Exec r16 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:16 end output========== .BG test line 13/job 17 finished (exit code: 0): ==========test:13:17 start output========== .TRY Exec r17 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:17 end output========== .BG test line 13/job 18 finished (exit code: 0): ==========test:13:18 start output========== .TRY Exec r18 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:18 end output========== .BG test line 13/job 19 finished (exit code: 0): ==========test:13:19 start output========== .TRY Exec r19 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:19 end output========== .BG test line 13/job 20 finished (exit code: 0): ==========test:13:20 start output========== .TRY Exec r20 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:20 end output========== .BG test line 13/job 21 finished (exit code: 0): ==========test:13:21 start output========== .TRY Exec r21 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:21 end output========== .BG test line 13/job 22 finished (exit code: 0): ==========test:13:22 start output========== .TRY Exec r22 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:22 end output========== .BG test line 13/job 23 finished (exit code: 0): ==========test:13:23 start output========== .TRY Exec r23 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:23 end output========== .BG test line 13/job 24 finished (exit code: 0): ==========test:13:24 start output========== .TRY Exec r24 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:24 end output========== .BG test line 13/job 25 finished (exit code: 0): ==========test:13:25 start output========== .TRY Exec r25 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:25 end output========== .BG test line 13/job 26 finished (exit code: 0): ==========test:13:26 start output========== .TRY Exec r26 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:26 end output========== .BG test line 13/job 27 finished (exit code: 0): ==========test:13:27 start output========== .TRY Exec r27 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:27 end output========== .BG test line 13/job 28 finished (exit code: 0): ==========test:13:28 start output========== .TRY Exec r28 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:28 end output========== .BG test line 13/job 29 finished (exit code: 0): ==========test:13:29 start output========== .TRY Exec r29 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:29 end output========== .BG test line 13/job 30 finished (exit code: 0): ==========test:13:30 start output========== .TRY Exec r30 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:30 end output========== .BG test line 13/job 31 finished (exit code: 0): ==========test:13:31 start output========== .TRY Exec r31 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:31 end output========== .BG test line 13/job 32 finished (exit code: 0): ==========test:13:32 start output========== .TRY Exec r32 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:32 end output========== .BG test line 13/job 33 finished (exit code: 0): ==========test:13:33 start output========== .TRY Exec r33 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:33 end output========== .BG test line 13/job 34 finished (exit code: 0): ==========test:13:34 start output========== .TRY Exec r34 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:34 end output========== .BG test line 13/job 35 finished (exit code: 0): ==========test:13:35 start output========== .TRY Exec r35 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:35 end output========== .BG test line 13/job 36 finished (exit code: 0): ==========test:13:36 start output========== .TRY Exec r36 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:36 end output========== .BG test line 13/job 37 finished (exit code: 0): ==========test:13:37 start output========== .TRY Exec r37 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:37 end output========== .BG test line 13/job 38 finished (exit code: 0): ==========test:13:38 start output========== .TRY Exec r38 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:38 end output========== .BG test line 13/job 39 finished (exit code: 0): ==========test:13:39 start output========== .TRY Exec r39 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:39 end output========== .BG test line 13/job 40 finished (exit code: 0): ==========test:13:40 start output========== .TRY Exec r40 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:40 end output========== .BG test line 13/job 41 finished (exit code: 0): ==========test:13:41 start output========== .TRY Exec r41 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:41 end output========== .BG test line 13/job 42 finished (exit code: 0): ==========test:13:42 start output========== .TRY Exec r42 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:42 end output========== .BG test line 13/job 43 finished (exit code: 0): ==========test:13:43 start output========== .TRY Exec r43 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:43 end output========== .BG test line 13/job 44 finished (exit code: 0): ==========test:13:44 start output========== .TRY Exec r44 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:44 end output========== .BG test line 13/job 45 finished (exit code: 0): ==========test:13:45 start output========== .TRY Exec r45 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:45 end output========== .BG test line 13/job 46 finished (exit code: 0): ==========test:13:46 start output========== .TRY Exec r46 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:46 end output========== .BG test line 13/job 47 finished (exit code: 0): ==========test:13:47 start output========== .TRY Exec r47 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:47 end output========== .BG test line 13/job 48 finished (exit code: 0): ==========test:13:48 start output========== .TRY Exec r48 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:48 end output========== .BG test line 13/job 49 finished (exit code: 0): ==========test:13:49 start output========== .TRY Exec r49 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:49 end output========== .BG test line 13/job 50 finished (exit code: 0): ==========test:13:50 start output========== .TRY Exec r50 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:50 end output========== .BG test line 13/job 51 finished (exit code: 0): ==========test:13:51 start output========== .TRY Exec r51 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:51 end output========== .BG test line 13/job 52 finished (exit code: 0): ==========test:13:52 start output========== .TRY Exec r52 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:52 end output========== .BG test line 13/job 53 finished (exit code: 0): ==========test:13:53 start output========== .TRY Exec r53 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:53 end output========== .BG test line 13/job 54 finished (exit code: 0): ==========test:13:54 start output========== .TRY Exec r54 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:54 end output========== .BG test line 13/job 55 finished (exit code: 0): ==========test:13:55 start output========== .TRY Exec r55 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:55 end output========== .BG test line 13/job 56 finished (exit code: 0): ==========test:13:56 start output========== .TRY Exec r56 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:56 end output========== .BG test line 13/job 57 finished (exit code: 0): ==========test:13:57 start output========== .TRY Exec r57 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:57 end output========== .BG test line 13/job 58 finished (exit code: 0): ==========test:13:58 start output========== .TRY Exec r58 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:58 end output========== .BG test line 13/job 59 finished (exit code: 0): ==========test:13:59 start output========== .TRY Exec r59 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:59 end output========== .BG test line 13/job 60 finished (exit code: 0): ==========test:13:60 start output========== .TRY Exec r60 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:60 end output========== .BG test line 13/job 61 finished (exit code: 0): ==========test:13:61 start output========== .TRY Exec r61 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:61 end output========== .BG test line 13/job 62 finished (exit code: 0): ==========test:13:62 start output========== .TRY Exec r62 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:62 end output========== .BG test line 13/job 63 finished (exit code: 0): ==========test:13:63 start output========== .TRY Exec r63 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:63 end output========== .BG test line 13/job 64 finished (exit code: 0): ==========test:13:64 start output========== .TRY Exec r64 op=monitor timeout=30000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:13:64 end output========== Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/metadata0000644000000000000000000000122112120057602026527 0ustar00usergroup00000000000000# list various meta-data %setenv LANG=POSIX %extcheck sort classes %extcheck ra-list.sh types class=ocf %extcheck ra-list.sh types class=lsb %extcheck ra-list.sh types class=heartbeat #%extcheck ra-list.sh #types class=stonith %extcheck xmllint.sh many classmeta class=ocf %extcheck xmllint.sh many classmeta class=lsb %extcheck xmllint.sh many classmeta class=heartbeat #%extcheck xmllint.sh many #classmeta class=stonith %extcheck xmllint.sh meta class=ocf type=Dummy %extcheck xmllint.sh meta class=lsb type=lrmregtest %extcheck xmllint.sh meta class=heartbeat type=Dummy #%extcheck xmllint.sh #meta class=stonith type=ssh provider class=ocf type=IPaddr Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/metadata.exp0000644000000000000000000000142512120057602027330 0ustar00usergroup00000000000000.SETENV LANG=POSIX .EXTCHECK sort .TRY List classes There are 4 RA classes supported: heartbeat lsb ocf stonith .EXTCHECK ra-list.sh .TRY List types class=ocf Cool. RA list passed. .EXTCHECK ra-list.sh .TRY List types class=lsb Cool. RA list passed. .EXTCHECK ra-list.sh .TRY List types class=heartbeat Cool. RA list passed. .EXTCHECK xmllint.sh many .TRY Meta-data class=ocf .EXTCHECK xmllint.sh many .TRY Meta-data class=lsb .EXTCHECK xmllint.sh many .TRY Meta-data class=heartbeat .EXTCHECK xmllint.sh .TRY Show meta-data class=ocf type=Dummy provider=heartbeat .EXTCHECK xmllint.sh .TRY Show meta-data class=lsb type=lrmregtest provider=heartbeat .EXTCHECK xmllint.sh .TRY Show meta-data class=heartbeat type=Dummy provider=heartbeat .TRY Show provider class=ocf type=IPaddr heartbeat Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/ra-list.sh0000755000000000000000000000025312120057602026742 0ustar00usergroup00000000000000#!/bin/sh awk ' NR==1 {num=$3;next} {in_num++} END{ if( num!=in_num ) print "ERROR: A mismatch in number of reported RAs!"; else print "Cool. RA list passed."; } ' Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/rscexec0000644000000000000000000000160712120057602026413 0ustar00usergroup00000000000000list # ocf %setenv dflt_rsc=rscexec_rsc_r1 add rsc=rscexec_rsc_r1 args="delay=0" list exec operation=start state exec operation=monitor exec operation=start exec operation=monitor exec operation=stop state exec operation=monitor exec operation=stop exec operation=monitor exec operation=meta-data del # lsb %setenv dflt_class=lsb dftl_rsc=rscexec_rsc_r1-lsb add exec operation=start state exec operation=monitor exec operation=start exec operation=monitor exec operation=stop state exec operation=monitor exec operation=stop exec operation=monitor exec operation=meta-data del %stop # stonith %setenv dflt_class=stonith dftl_rsc=rscexec_rsc_r1-stonith add type=null args="hostlist=node1" exec operation=start state exec operation=monitor exec operation=start exec operation=monitor exec operation=stop state exec operation=monitor exec operation=stop exec operation=monitor exec operation=meta-data del Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/rscexec.exp0000644000000000000000000001113512120057602027203 0ustar00usergroup00000000000000.TRY List resources Currently no resources are managed by LRM. .SETENV dflt_rsc=rscexec_rsc_r1 .TRY Add resource rscexec_rsc_r1 class=ocf type=lrmregtest provider=heartbeat args=delay=0 Succeeded in adding this resource. .TRY List resources Resource ID:rscexec_rsc_r1 Resource agent class:ocf Resource agent type:lrmregtest Resource agent provider:heartbeat Resource agent parameters:delay=0 .TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] .TRY Show state rscexec_rsc_r1 resource state:LRM_RSC_IDLE The resource 1 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=0 .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Show state rscexec_rsc_r1 resource state:LRM_RSC_IDLE The resource 3 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=0 operation 'monitor' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=0 operation 'stop' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=0 .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=7): [null] .TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=7): [null] .TRY Exec rscexec_rsc_r1 op=meta-data timeout=1000 interval=0 target=EVERYTIME args= > meta-data succeed (status=0,rc=0): [null] .TRY Delete resource rscexec_rsc_r1 Succeeded in deleting this resource. .SETENV dflt_class=lsb dftl_rsc=rscexec_rsc_r1-lsb .TRY Add resource rscexec_rsc_r1 class=lsb type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] .TRY Show state rscexec_rsc_r1 resource state:LRM_RSC_IDLE The resource 1 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Show state rscexec_rsc_r1 resource state:LRM_RSC_IDLE The resource 3 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: operation 'monitor' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: operation 'stop' [call_id=(removed)]: start_delay=0, interval=0, timeout=1000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=7): [null] .TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=7): [null] .TRY Exec rscexec_rsc_r1 op=meta-data timeout=1000 interval=0 target=EVERYTIME args= > meta-data succeed (status=0,rc=0): [null] .TRY Delete resource rscexec_rsc_r1 Succeeded in deleting this resource. Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/rscmgmt0000644000000000000000000000054612120057602026434 0ustar00usergroup00000000000000list # add/remove resources # add rsc=r1 info rsc=r1 list del rsc=r1 %setenv dflt_class=lsb dflt_type=lrmregtest list add rsc=r1 list del rsc=r1 list # # a bit of mix # %setenv dflt_class=ocf add rsc=r1 add rsc=r1 class=lsb type=lrmregtest add rsc=r1 del rsc=r1 add rsc=r1 class=lsb type=lrmregtest list add rsc=r2 list del rsc=r1 del rsc=r2 list del rsc=r1 Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/rscmgmt.exp0000644000000000000000000000466712120057602027237 0ustar00usergroup00000000000000.TRY List resources Currently no resources are managed by LRM. .TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY Show info r1 Resource ID:r1 Resource agent class:ocf Resource agent type:lrmregtest Resource agent provider:heartbeat .TRY List resources Resource ID:r1 Resource agent class:ocf Resource agent type:lrmregtest Resource agent provider:heartbeat .TRY Delete resource r1 Succeeded in deleting this resource. .SETENV dflt_class=lsb dflt_type=lrmregtest .TRY List resources Currently no resources are managed by LRM. .TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY List resources Resource ID:r1 Resource agent class:lsb Resource agent type:lrmregtest Resource agent provider:heartbeat .TRY Delete resource r1 Succeeded in deleting this resource. .TRY List resources Currently no resources are managed by LRM. .SETENV dflt_class=ocf .TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args= ERROR: lrm_add_rsc(): got a return code HA_FAIL from a reply message of addrsc with function get_ret_from_msg. Failed to add this resource. .TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args= ERROR: lrm_add_rsc(): got a return code HA_FAIL from a reply message of addrsc with function get_ret_from_msg. Failed to add this resource. .TRY Delete resource r1 Succeeded in deleting this resource. .TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY List resources Resource ID:r1 Resource agent class:lsb Resource agent type:lrmregtest Resource agent provider:heartbeat .TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args= Succeeded in adding this resource. .TRY List resources Resource ID:r2 Resource agent class:ocf Resource agent type:lrmregtest Resource agent provider:heartbeat Resource ID:r1 Resource agent class:lsb Resource agent type:lrmregtest Resource agent provider:heartbeat .TRY Delete resource r1 Succeeded in deleting this resource. .TRY Delete resource r2 Succeeded in deleting this resource. .TRY List resources Currently no resources are managed by LRM. .TRY Delete resource r1 ERROR: lrm_delete_rsc(): got a return code HA_FAIL from a reply message of delrsc with function get_ret_from_msg. Failed to delete this resource. Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/rscmgmt.log_filter0000755000000000000000000000043012120057602030554 0ustar00usergroup00000000000000#!/bin/sh awk ' n<2 && /ERROR: on_msg_add_rsc: same id resource exists./ {n++; next} m<1 && /ERROR: on_msg_del_rsc: no rsc with id/ {m++; next} {print} END{ if( n!=2 ) print "ERROR: missed on_msg_add_rsc errors"; if( m!=1 ) print "ERROR: missed on_msg_del_rsc errors"; } ' Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/serialize0000644000000000000000000000127612120057602026750 0ustar00usergroup00000000000000list # allow for a delay of 2 seconds %setenv dflt_timeout=2500 add rsc=r1 args="delay=2" # # we run the next three ops in the background # in case ops are not serialized, the lrmregtest RA should complain # %bg 2 exec operation=start # insert sleeps to make sure that the operations are started in # the order given here %sleep 1 # set timeouts high enough so that no op fails exec operation=start timeout=3000 %sleep 1 %bgrepeat 4 exec operation=monitor timeout=11000 %sleep 11 state exec operation=stop state del rsc=r1 # # # %setenv dflt_rsc=r2 dflt_timeout=10500 add rsc=r2 args="ignore_TERM=1 delay=9" exec operation=start %bg exec operation=monitor timeout=500 exec operation=monitor del rsc=r2 Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/serialize.exp0000644000000000000000000000761712120057602027550 0ustar00usergroup00000000000000.TRY List resources Currently no resources are managed by LRM. .SETENV dflt_timeout=2500 .TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=delay=2 Succeeded in adding this resource. .BG 2 .BG test line 10/job 1 runs in background .SLEEP 1 .BG test line 15/job 2 runs in background .SLEEP 1 .BGREPEAT 4 .BG test line 18/job 1 runs in background .BG test line 18/job 2 runs in background .BG test line 18/job 3 runs in background .BG test line 18/job 4 runs in background .SLEEP 11 .TRY Show state r1 resource state:LRM_RSC_IDLE The resource 2 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=3000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=2 operation 'monitor' [call_id=(removed)]: start_delay=0, interval=0, timeout=11000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=2 .TRY Exec r1 op=stop timeout=2500 interval=0 target=EVERYTIME args= > stop succeed (status=0,rc=0): [null] .TRY Show state r1 resource state:LRM_RSC_IDLE The resource 3 operations' information: operation 'start' [call_id=(removed)]: start_delay=0, interval=0, timeout=3000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=2 operation 'monitor' [call_id=(removed)]: start_delay=0, interval=0, timeout=11000, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=2 operation 'stop' [call_id=(removed)]: start_delay=0, interval=0, timeout=2500, app_name=lrmadmin rc=0 (ok), op_status=0 (succeed) parameters: delay=2 .TRY Delete resource r1 Succeeded in deleting this resource. .SETENV dflt_rsc=r2 dflt_timeout=10500 .TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args=ignore_TERM=1 delay=9 Succeeded in adding this resource. .TRY Exec r2 op=start timeout=10500 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] .BG .BG test line 31/job 1 runs in background .TRY Exec r2 op=monitor timeout=10500 interval=0 target=EVERYTIME args= ERROR: This operation has timed out - no result from lrmd. .TRY Delete resource r2 Succeeded in deleting this resource. .BG test line 10/job 1 finished (exit code: 0): ==========test:10:1 start output========== .TRY Exec r1 op=start timeout=2500 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:10:1 end output========== .BG test line 15/job 2 finished (exit code: 0): ==========test:15:2 start output========== .TRY Exec r1 op=start timeout=3000 interval=0 target=EVERYTIME args= > start succeed (status=0,rc=0): [null] ==========test:15:2 end output========== .BG test line 18/job 1 finished (exit code: 0): ==========test:18:1 start output========== .TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:18:1 end output========== .BG test line 18/job 2 finished (exit code: 0): ==========test:18:2 start output========== .TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:18:2 end output========== .BG test line 18/job 3 finished (exit code: 0): ==========test:18:3 start output========== .TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:18:3 end output========== .BG test line 18/job 4 finished (exit code: 0): ==========test:18:4 start output========== .TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args= > monitor succeed (status=0,rc=0): [null] ==========test:18:4 end output========== .BG test line 31/job 1 finished (exit code: 0): ==========test:31:1 start output========== .TRY Exec r2 op=monitor timeout=500 interval=0 target=EVERYTIME args= ERROR: This operation has timed out - no result from lrmd. ==========test:31:1 end output========== Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/stonith0000644000000000000000000000015112120057602026440 0ustar00usergroup00000000000000%extcheck xmllint.sh many %shell stonith -L | while read p; do echo $p:heartbeat; stonith -m -t $p; done Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/stonith.exp0000644000000000000000000000015112120057602027233 0ustar00usergroup00000000000000.EXTCHECK xmllint.sh many .SHELL stonith -L | while read p; do echo $p:heartbeat; stonith -m -t $p; done Reusable-Cluster-Components-glue--3cff550e1084/lrm/test/testcases/xmllint.sh0000755000000000000000000000055012120057602027056 0ustar00usergroup00000000000000#!/bin/sh gawk -v many="$1" ' BEGIN{XMLLINT="xmllint --noout -";} function chkoutput(ra) { if( ra=="" ) return; if( close(XMLLINT) ) # we need gawk for this print "xmllint reported error in RA:",ra; } many=="many" && /^[a-zA-Z][^:]*:[a-zA-Z0-9]+$/ { chkoutput(ra); ra=$0; next; } { print | XMLLINT } END{ if( many!="many" ) chkoutput("noname"); } ' Reusable-Cluster-Components-glue--3cff550e1084/replace/Makefile.am0000644000000000000000000000202312120057602024725 0ustar00usergroup00000000000000# # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \ -I$(top_srcdir)/linux-ha -I$(top_builddir)/linux-ha QUIET_LIBTOOL_OPTS = @QUIET_LIBTOOL_OPTS@ LIBTOOL = @LIBTOOL@ @QUIET_LIBTOOL_OPTS@ noinst_LTLIBRARIES = libreplace.la libreplace_la_SOURCES = libreplace_la_LIBADD = @LTLIBOBJS@ Reusable-Cluster-Components-glue--3cff550e1084/replace/NoSuchFunctionName.c0000644000000000000000000000206612120057602026552 0ustar00usergroup00000000000000/* * Copyright (C) 2002 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ void nosuchfunctionname(void); /* * This is a completely useless function put here only to make OpenBSD make * procedures happy. I hope no one ever makes such a function ;-) */ void nosuchfunctionname(void) { return; } Reusable-Cluster-Components-glue--3cff550e1084/replace/alphasort.c0000644000000000000000000000270312120057602025037 0ustar00usergroup00000000000000/* * * alphasort - replacement for alphasort functions. * * Matt Soffen * Copyright (C) 2001 Matt Soffen * * Taken from the FreeBSD file (with copyright notice) * /usr/src/gnu/lib/libdialog/dir.c *************************************************************************** * Program: dir.c * Author: Marc van Kempen * desc: Directory routines, sorting and reading * * Copyright (c) 1995, Marc van Kempen * * All rights reserved. * * This software may be used, modified, copied, distributed, and * sold, in both source and binary form provided that the above * copyright and these terms are retained, verbatim, as the first * lines of this file. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with * its use. * *************************************************************************** */ #include #include #include #include /* XXX for _POSIX_VERSION ifdefs */ #if HAVE_STRINGS_H #include #endif #if !defined sgi && !defined _POSIX_VERSION #include #endif #include #include #include #include int alphasort(const void *dirent1, const void *dirent2) { return(strcmp((*(const struct dirent **)dirent1)->d_name, (*(const struct dirent **)dirent2)->d_name)); } Reusable-Cluster-Components-glue--3cff550e1084/replace/daemon.c0000644000000000000000000000535712120057602024315 0ustar00usergroup00000000000000/*- * * daemon - replacement for daemon function. * * Matt Soffen * Copyright (C) 2004 Matt Soffen * * Taken from the FreeBSD file (with copyright notice) * ------------------------------------------------------------ * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/lib/libc/gen/daemon.c,v 1.3 2000/01/27 23:06:14 jasone Exp $ * */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include #include #include int daemon(nochdir, noclose) int nochdir, noclose; { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close(fd); } return (0); } Reusable-Cluster-Components-glue--3cff550e1084/replace/inet_pton.c0000644000000000000000000001321212120057602025036 0ustar00usergroup00000000000000/* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Chris Wright June 22, 2001 * Merged contents of inet_pton.c from Apache2.0.16 and BIND8 * The Apache base is more portable within heartbeat's envrionment, * however, the BIND8 version has two small logic changes that are * newer. */ #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #include #include #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ sizeof(short) #endif #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef __P #define __P(x) x #endif /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4 __P((const char *src, unsigned char *dst)); #if HAVE_IPV6 static int inet_pton6 __P((const char *src, unsigned char *dst)); #endif /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char *src, void *dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); #if HAVE_IPV6 case AF_INET6: return (inet_pton6(src, dst)); #endif default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, unsigned char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; unsigned char tmp[INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { unsigned int new = *tp * 10 + (pch - digits); if (new > 255) return (0); *tp = new; if (! saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, INADDRSZ); return (1); } #if HAVE_IPV6 /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, unsigned char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit; unsigned int val; memset((tp = tmp), '\0', IN6ADDRSZ); endp = tp + IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') { return (0); } if (tp + INT16SZ > endp) return (0); *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + INT16SZ > endp) return (0); *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, IN6ADDRSZ); return (1); } #endif /* HAVE_IPV6 */ Reusable-Cluster-Components-glue--3cff550e1084/replace/scandir.c0000644000000000000000000001647012120057602024473 0ustar00usergroup00000000000000/* scandir: Scan a directory, collecting all (selected) items into a an array. * * This code borrowed from 'libit', which can be found here: * * http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ * * The original author put this code in the public domain. * It has been modified slightly to get rid of warnings, etc. * * Below is the email I received from pinard@iro.umontreal.ca (François Pinard) * when I sent him an email asking him about the license, etc. of this * code which I obtained from his site. * * I think the correct spelling of his name is Rich Salz. I think he's now * rsalz@datapower.com... * -- * Rich Salz, Chief Security Architect * DataPower Technology http://www.datapower.com * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html * * Copyright(C): none (public domain) * License: none (public domain) * Author: Rich Salz * * * * -- Alan Robertson * alanr@unix.sh * ************************************************************************** * * Subject: Re: Scandir replacement function * Date: 18 May 2001 12:00:48 -0400 * From: pinard@iro.umontreal.ca (François Pinard) * To: Alan Robertson * References: 1 * * * [Alan Robertson] * * > Hi, I'd like to use your scandir replacement function found here: * > http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ But, it does * > not indicate authorship or licensing terms in it. Could you tell me * > who wrote this code, under what license you distribute it, and whether * > and under what terms I may further distribute it? * * Hello, Alan. These are (somewhat) explained in UNSHAR.HDR found in the * same directory. The routines have been written by Rick Saltz (I'm not * completely sure of the spelling) a long while ago. I think that nowadays, * Rick is better known as the main author of the nice INN package. * ************************************************************************** * * I spent a little time verifying this with Rick Salz. * The results are below: * ************************************************************************** * * Date: Tue, 20 Sep 2005 21:52:09 -0400 (EDT) * From: Rich Salz * To: Alan Robertson * Subject: Re: Verifying permissions/licenses/etc on some old code of yours - * scandir.c * In-Reply-To: <433071CA.8000107@unix.sh> * Message-ID: * Content-Type: TEXT/PLAIN; charset=US-ASCII * * yes, it's most definitely in the public domain. * * I'm glad you find it useful. I'm surprised it hasn't been replaced by, * e.g,. something in GLibC. Ii'm impressed you tracked me down. * * /r$ * * -- * Rich Salz Chief Security Architect * DataPower Technology http://www.datapower.com * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html * ----------------------------------------------------------------------> * Subject: scandir, ftw REDUX * Date: 1 Jan 88 00:47:01 GMT * From: rsalz@pebbles.bbn.com * Newsgroups: comp.sources.misc * * * Forget my previous message -- I just decided for completeness's sake to * implement the SysV ftw(3) routine, too. * * To repeat, these are public-domain implementations of the SystemV ftw() * routine, the BSD scandir() and alphasort() routines, and documentation for * same. The FTW manpage could be more readable, but so it goes. * * Anyhow, feel free to post these, and incorporate them into your existing * packages. I have readdir() routiens for MSDOS and the Amiga if anyone * wants them, and should have them for VMS by the end of January; let me * know if you want copies. * * Yours in filesystems, * /r$ * * Anyhow, feel free to post * ----------------------------------------------------------------------< * */ #include #include #include #include #include #include #ifndef NULL # define NULL ((void *) 0) #endif /* Initial guess at directory allocated. */ #define INITIAL_ALLOCATION 20 int scandir (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT /* This is what the Linux man page says */ int (*compare_function) (const struct dirent**, const struct dirent**) #else /* This is what the Linux header file says ... */ int (*compare_function) (const void *, const void *) #endif ); int scandir (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT /* This is what the linux man page says */ int (*compare_function) (const struct dirent**, const struct dirent**) #else /* This is what the linux header file says ... */ int (*compare_function) (const void *, const void *) #endif ) { DIR *directory; struct dirent **array; struct dirent *entry; struct dirent *copy; int allocated = INITIAL_ALLOCATION; int counter = 0; /* Get initial list space and open directory. */ if (directory = opendir (directory_name), directory == NULL) return -1; if (array = (struct dirent **) malloc (allocated * sizeof (struct dirent *)), array == NULL) return -1; /* Read entries in the directory. */ while (entry = readdir (directory), entry) if (select_function == NULL || (*select_function) (entry)) { /* User wants them all, or he wants this one. Copy the entry. */ /* * On some OSes the declaration of "entry->d_name" is a minimal-length * placeholder. Example: Solaris: * /usr/include/sys/dirent.h: * "char d_name[1];" * man page "dirent(3)": * The field d_name is the beginning of the character array * giving the name of the directory entry. This name is * null terminated and may have at most MAXNAMLEN chars. * So our malloc length may need to be increased accordingly. * sizeof(entry->d_name): space (possibly minimal) in struct. * strlen(entry->d_name): actual length of the entry. * * John Kavadias * David Lee */ int namelength = strlen(entry->d_name) + 1; /* length with NULL */ int extra = 0; if (sizeof(entry->d_name) <= namelength) { /* allocated space <= required space */ extra += namelength - sizeof(entry->d_name); } if (copy = (struct dirent *) malloc (sizeof (struct dirent) + extra), copy == NULL) { closedir (directory); free (array); return -1; } copy->d_ino = entry->d_ino; copy->d_reclen = entry->d_reclen; strcpy (copy->d_name, entry->d_name); /* Save the copy. */ if (counter + 1 == allocated) { allocated <<= 1; array = (struct dirent **) realloc ((char *) array, allocated * sizeof (struct dirent *)); if (array == NULL) { closedir (directory); free (array); free (copy); return -1; } } array[counter++] = copy; } /* Close things off. */ array[counter] = NULL; *array_pointer = array; closedir (directory); /* Sort? */ if (counter > 1 && compare_function) qsort ((char *) array, counter, sizeof (struct dirent *) , (int (*)(const void *, const void *))(compare_function)); return counter; } Reusable-Cluster-Components-glue--3cff550e1084/replace/setenv.c0000644000000000000000000000257412120057602024354 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include /* * Small replacement function for setenv() */ int setenv(const char *name, const char * value, int why) { int rc = -1; if ( name && value ) { char * envp = NULL; envp = malloc(strlen(name)+strlen(value)+2); if (envp) { /* * Unfortunately, the putenv API guarantees memory leaks when * changing environment variables repeatedly... :-( */ sprintf(envp, "%s=%s", name, value); /* Cannot free envp (!) */ rc = putenv(envp); } } return(rc); } Reusable-Cluster-Components-glue--3cff550e1084/replace/strerror.c0000644000000000000000000000217212120057602024724 0ustar00usergroup00000000000000/* * Copyright (C) 2002 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include extern const char * sys_err[]; extern int sys_nerr; char * strerror(int errnum) { static char whaterr[32]; if (errnum < 0) { return "negative errno"; } if (errnum >= sys_nerr) { snprintf(whaterr, sizeof(whaterr),"error %d", errnum); return whaterr; } return sys_err[sys_nerr]; } Reusable-Cluster-Components-glue--3cff550e1084/replace/strlcat.c0000644000000000000000000000220512120057602024513 0ustar00usergroup00000000000000#include #include /* * Copyright (C) 2007 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ size_t strlcat(char *dest, const char * src, size_t maxlen) { size_t curlen = strlen(dest); size_t addlen = strlen(src); size_t appendlen = (maxlen-1) - curlen; if (appendlen > 0) { strlcpy(dest+curlen, src, maxlen-curlen); } return curlen + addlen; } Reusable-Cluster-Components-glue--3cff550e1084/replace/strlcpy.c0000644000000000000000000000207112120057602024540 0ustar00usergroup00000000000000#include #include /* * Copyright (C) 2007 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ size_t strlcpy(char *dest, const char * src, size_t maxlen) { size_t srclen = strlen(src); if (maxlen > 0) { strncpy(dest, src, maxlen); dest[maxlen-1]=EOS; } return srclen; } Reusable-Cluster-Components-glue--3cff550e1084/replace/strndup.c0000644000000000000000000000224512120057602024542 0ustar00usergroup00000000000000#include #include #include /* * Copyright (C) 2004 Matt Soffen * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Taken from the GlibC implementation of strndup */ char *strndup(const char *str, size_t len) { size_t n = strnlen(str,len); char *new = (char *) malloc (len+1); if (NULL == new) { return NULL; } new[n] = '\0'; return (char *)memcpy (new, str, len); } Reusable-Cluster-Components-glue--3cff550e1084/replace/strnlen.c0000644000000000000000000000205212120057602024524 0ustar00usergroup00000000000000#include #include /* * Copyright (C) 2003 Alan Robertson * This software licensed under the GNU LGPL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ size_t strnlen(const char *s, size_t maxlen) { const char * eospos; eospos = memchr(s, (int)'\0', maxlen); return (eospos == NULL ? maxlen : (size_t)(eospos-s)); } Reusable-Cluster-Components-glue--3cff550e1084/replace/unsetenv.c0000644000000000000000000000264212120057602024713 0ustar00usergroup00000000000000/* * Copyright (C) 2001 Alan Robertson * This software licensed under the GNU LGPL. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #define __environ environ #ifndef HAVE_ENVIRON_DECL extern char **environ; #endif int unsetenv (const char *name) { const size_t len = strlen (name); char **ep; for (ep = __environ; *ep; ++ep) { if (!strncmp (*ep, name, len) && (*ep)[len] == '=') { /* Found it. */ /* Remove this pointer by moving later ones back. */ char **dp = ep; do dp[0] = dp[1]; while (*dp++); /* Continue the loop in case NAME appears again. */ } } return 0; } Reusable-Cluster-Components-glue--3cff550e1084/replace/uuid_parse.c0000644000000000000000000002564012120057602025207 0ustar00usergroup00000000000000/* * uuid: emulation of e2fsprogs interface if implementation lacking. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original uuid implementation: copyright (C) Theodore Ts'o * * This importation into heartbeat: * Copyright (C) 2004 David Lee * */ #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include #include /* * Local "replace" implementation of uuid functions. */ #include #include #include /* UUID Variant definitions */ #define UUID_VARIANT_NCS 0 #define UUID_VARIANT_DCE 1 #define UUID_VARIANT_MICROSOFT 2 #define UUID_VARIANT_OTHER 3 /* UUID Type definitions */ #define UUID_TYPE_DCE_TIME 1 #define UUID_TYPE_DCE_RANDOM 4 /* For uuid_compare() */ #define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); /************************************ * Private types ************************************/ #define longlong long long /* * Offset between 15-Oct-1582 and 1-Jan-70 */ #define TIME_OFFSET_HIGH 0x01B21DD2 #define TIME_OFFSET_LOW 0x13814000 #if (SIZEOF_INT == 4) typedef unsigned int __u32; #elif (SIZEOF_LONG == 4) typedef unsigned long __u32; #endif #if (SIZEOF_INT == 2) typedef int __s16; typedef unsigned int __u16; #elif (SIZEOF_SHORT == 2) typedef short __s16; typedef unsigned short __u16; #endif typedef unsigned char __u8; struct uuid { __u32 time_low; __u16 time_mid; __u16 time_hi_and_version; __u16 clock_seq; __u8 node[6]; }; /************************************ * internal routines ************************************/ static void uuid_pack(const struct uuid *uu, uuid_t ptr) { __u32 tmp; unsigned char *out = ptr; tmp = uu->time_low; out[3] = (unsigned char) tmp; tmp >>= 8; out[2] = (unsigned char) tmp; tmp >>= 8; out[1] = (unsigned char) tmp; tmp >>= 8; out[0] = (unsigned char) tmp; tmp = uu->time_mid; out[5] = (unsigned char) tmp; tmp >>= 8; out[4] = (unsigned char) tmp; tmp = uu->time_hi_and_version; out[7] = (unsigned char) tmp; tmp >>= 8; out[6] = (unsigned char) tmp; tmp = uu->clock_seq; out[9] = (unsigned char) tmp; tmp >>= 8; out[8] = (unsigned char) tmp; memcpy(out+10, uu->node, 6); } static void uuid_unpack(const uuid_t in, struct uuid *uu) { const __u8 *ptr = in; __u32 tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_low = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_mid = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_hi_and_version = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->clock_seq = tmp; memcpy(uu->node, ptr, 6); } /************************************ * Main routines, except uuid_generate*() ************************************/ void uuid_clear(uuid_t uu) { memset(uu, 0, 16); } int uuid_compare(const uuid_t uu1, const uuid_t uu2) { struct uuid uuid1, uuid2; uuid_unpack(uu1, &uuid1); uuid_unpack(uu2, &uuid2); UUCMP(uuid1.time_low, uuid2.time_low); UUCMP(uuid1.time_mid, uuid2.time_mid); UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); UUCMP(uuid1.clock_seq, uuid2.clock_seq); return memcmp(uuid1.node, uuid2.node, 6); } void uuid_copy(uuid_t dst, const uuid_t src) { unsigned char *cp1; const unsigned char *cp2; int i; for (i=0, cp1 = dst, cp2 = src; i < 16; i++) *cp1++ = *cp2++; } /* if uu is the null uuid, return 1 else 0 */ int uuid_is_null(const uuid_t uu) { const unsigned char *cp; int i; for (i=0, cp = uu; i < 16; i++) if (*cp++) return 0; return 1; } /* 36byte-string=>uuid */ int uuid_parse(const char *in, uuid_t uu) { struct uuid uuid; int i; const char *cp; char buf[3]; if (strlen(in) != 36) return -1; for (i=0, cp = in; i <= 36; i++,cp++) { if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { if (*cp == '-') continue; else return -1; } if (i== 36) if (*cp == 0) continue; if (!isxdigit((int) *cp)) return -1; } uuid.time_low = strtoul(in, NULL, 16); uuid.time_mid = strtoul(in+9, NULL, 16); uuid.time_hi_and_version = strtoul(in+14, NULL, 16); uuid.clock_seq = strtoul(in+19, NULL, 16); cp = in+24; buf[2] = 0; for (i=0; i < 6; i++) { buf[0] = *cp++; buf[1] = *cp++; uuid.node[i] = strtoul(buf, NULL, 16); } uuid_pack(&uuid, uu); return 0; } /* uuid=>36byte-string-with-null */ void uuid_unparse(const uuid_t uu, char *out) { struct uuid uuid; uuid_unpack(uu, &uuid); sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5]); } /************************************ * Main routines: uuid_generate*() ************************************/ #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_SRANDOM #define srand(x) srandom(x) #define rand() random() #endif static int get_random_fd(void) { struct timeval tv; static int fd = -2; int i; if (fd == -2) { gettimeofday(&tv, 0); fd = open("/dev/urandom", O_RDONLY); if (fd == -1) fd = open("/dev/random", O_RDONLY | O_NONBLOCK); srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); } /* Crank the random number generator a few times */ gettimeofday(&tv, 0); for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) rand(); return fd; } /* * Generate a series of random bytes. Use /dev/urandom if possible, * and if not, use srandom/random. */ static void get_random_bytes(void *buf, int nbytes) { int i, n = nbytes, fd = get_random_fd(); int lose_counter = 0; unsigned char *cp = (unsigned char *) buf; if (fd >= 0) { while (n > 0) { i = read(fd, cp, n); if (i <= 0) { if (lose_counter++ > 16) break; continue; } n -= i; cp += i; lose_counter = 0; } } /* * We do this all the time, but this is the only source of * randomness if /dev/random/urandom is out to lunch. */ for (cp = buf, i = 0; i < nbytes; i++) *cp++ ^= (rand() >> 7) & 0xFF; return; } /* * Get the ethernet hardware address, if we can find it... */ static int get_node_id(unsigned char *node_id) { #ifdef HAVE_NET_IF_H int sd; struct ifreq ifr, *ifrp; struct ifconf ifc; char buf[1024]; int n, i; unsigned char *a; /* * BSD 4.4 defines the size of an ifreq to be * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len * However, under earlier systems, sa_len isn't present, so the size is * just sizeof(struct ifreq) */ #ifdef HAVE_SA_LEN #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #define ifreq_size(i) max(sizeof(struct ifreq),\ sizeof((i).ifr_name)+(i).ifr_addr.sa_len) #else #define ifreq_size(i) sizeof(struct ifreq) #endif /* HAVE_SA_LEN*/ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sd < 0) { return -1; } memset(buf, 0, sizeof(buf)); ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { close(sd); return -1; } n = ifc.ifc_len; for (i = 0; i < n; i+= ifreq_size(*ifr) ) { ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); #ifdef SIOCGIFHWADDR if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) continue; a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; #else #ifdef SIOCGENADDR if (ioctl(sd, SIOCGENADDR, &ifr) < 0) continue; a = (unsigned char *) ifr.ifr_enaddr; #else /* * XXX we don't have a way of getting the hardware * address */ close(sd); return 0; #endif /* SIOCGENADDR */ #endif /* SIOCGIFHWADDR */ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) continue; if (node_id) { memcpy(node_id, a, 6); close(sd); return 1; } } close(sd); #endif return 0; } /* Assume that the gettimeofday() has microsecond granularity */ #define MAX_ADJUSTMENT 10 static int get_clock(__u32 *clock_high, __u32 *clock_low, __u16 *ret_clock_seq) { static int adjustment = 0; static struct timeval last = {0, 0}; static __u16 clock_seq; struct timeval tv; unsigned longlong clock_reg; try_again: gettimeofday(&tv, 0); if ((last.tv_sec == 0) && (last.tv_usec == 0)) { get_random_bytes(&clock_seq, sizeof(clock_seq)); clock_seq &= 0x1FFF; last = tv; last.tv_sec--; } if ((tv.tv_sec < last.tv_sec) || ((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec))) { clock_seq = (clock_seq+1) & 0x1FFF; adjustment = 0; last = tv; } else if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) { if (adjustment >= MAX_ADJUSTMENT) goto try_again; adjustment++; } else { adjustment = 0; last = tv; } clock_reg = tv.tv_usec*10 + adjustment; clock_reg += ((unsigned longlong) tv.tv_sec)*10000000; clock_reg += (((unsigned longlong) 0x01B21DD2) << 32) + 0x13814000; *clock_high = clock_reg >> 32; *clock_low = clock_reg; *ret_clock_seq = clock_seq; return 0; } /* create a new uuid, based on randomness */ void uuid_generate_random(uuid_t out) { uuid_t buf; struct uuid uu; get_random_bytes(buf, sizeof(buf)); uuid_unpack(buf, &uu); uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; uuid_pack(&uu, out); } /* create a new uuid, based on time */ static void uuid_generate_time(uuid_t out) { static unsigned char node_id[6]; static int has_init = 0; struct uuid uu; __u32 clock_mid; if (!has_init) { if (get_node_id(node_id) <= 0) { get_random_bytes(node_id, 6); /* * Set multicast bit, to prevent conflicts * with IEEE 802 addresses obtained from * network cards */ node_id[0] |= 0x80; } has_init = 1; } get_clock(&clock_mid, &uu.time_low, &uu.clock_seq); uu.clock_seq |= 0x8000; uu.time_mid = (__u16) clock_mid; uu.time_hi_and_version = (clock_mid >> 16) | 0x1000; memcpy(uu.node, node_id, 6); uuid_pack(&uu, out); } void uuid_generate(uuid_t out) { if (get_random_fd() >= 0) { uuid_generate_random(out); }else{ uuid_generate_time(out); } }